library(swimplot) library(grid) library(gtable) library(readr) library(mosaic) library(forcats) library(dplyr) library(survival) library(survminer) library(ggplot2) library(scales) library(coxphf) library(ggthemes) library(tidyverse) library(gtsummary) library(flextable) library(reshape2) library(parameters) library(car) library(ComplexHeatmap) library(tidyverse) library(readxl) library(janitor) library(DT) library(pROC) library(rms)

#ctDNA Detection Rates by Window and Stages

#ctDNA at Baseline
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_data$ctDNA.Base <- factor(circ_data$ctDNA.Base, levels=c("NEGATIVE","POSITIVE"))
circ_data <- subset(circ_data, ctDNA.Base %in% c("NEGATIVE", "POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("I/II","III/IVA/IVB","IVC"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.Base == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.Base, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.Base == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
        Stage Total_Count Positive_Count    Rate
1        I/II          34             32  94.12%
2 III/IVA/IVB          27             22  81.48%
3         IVC           2              2 100.00%
4     Overall          63             56  88.89%
#ctDNA at MRD
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("I/II","III/IVA/IVB","IVC"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.MRD == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.MRD, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.MRD == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
        Stage Total_Count Positive_Count   Rate
1        I/II          34              5 14.71%
2 III/IVA/IVB          33              7 21.21%
3         IVC           2              1 50.00%
4     Overall          69             13 18.84%
#ctDNA at Surveillance
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("I/II","III/IVA/IVB","IVC"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.Surveillance == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
        Stage Total_Count Positive_Count   Rate
1        I/II          42             10 23.81%
2 III/IVA/IVB          35             13 37.14%
3         IVC           2              1 50.00%
4     Overall          79             24 30.38%

#Demographics Table

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]

circ_data_subset <- circ_data %>%
  select(
    Sex,
    Age,
    Tobacco.History,
    Prim.Location,
    cT,
    cN,
    cM,
    Histology,
    Stage,
    p16.status,
    Treatment.Group,
    PFS.Event,
    OS.Event,
    OS.months) %>%
  mutate(
    Sex = factor(Sex),
    Age = as.numeric(Age),
    Tobacco.History = factor(Tobacco.History),
    Prim.Location = factor(Prim.Location),
    cT = factor(cT),
    cN = factor(cN),
    cM = factor(cM),
    Histology = factor(Histology),
    Stage = factor(Stage),
    p16.status = factor(p16.status),
    Treatment.Group = factor(Treatment.Group),
    PFS.Event = factor(PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression")),
    OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
    OS.months = as.numeric(OS.months)) 
table1 <- circ_data_subset %>%
  tbl_summary(
    statistic = list(
      all_continuous() ~ "{median} ({min} - {max})",
      all_categorical() ~ "{n} ({p}%)")) %>%
  bold_labels()
table1
Characteristic N = 971
Sex
    Female 17 (18%)
    Male 80 (82%)
Age 67 (30 - 96)
Tobacco.History 63 (65%)
Prim.Location
    Larynx/Hypopharynx 5 (5.2%)
    Oral cavity 16 (16%)
    Oropharynx 67 (69%)
    Other (paranasal sinus and nasopharyngeal) 9 (9.3%)
cT
    T0 2 (2.1%)
    T1 12 (12%)
    T2 31 (32%)
    T3 30 (31%)
    T4 21 (22%)
    TX 1 (1.0%)
cN
    N0 22 (23%)
    N1 33 (34%)
    N2 33 (34%)
    N3 9 (9.3%)
cM
    M0 93 (96%)
    M1 4 (4.1%)
Histology
    Adenosquamous carcinoma 1 (1.0%)
    Basaloid squamous cell carcinoma 6 (6.2%)
    Epithelial myoepithelial carcinoma 1 (1.0%)
    Squamous cell carcinoma 86 (89%)
    Undifferentiated carcinoma 3 (3.1%)
Stage
    I/II 49 (51%)
    III/IVA/IVB 45 (46%)
    IVC 3 (3.1%)
p16.status
    Negative 43 (44%)
    Positive 54 (56%)
Treatment.Group
    Definitive CRT or RT 69 (71%)
    None (Declined Treatment) 1 (1.0%)
    None (Hospice) 2 (2.1%)
    Surgery + CRT or RT 24 (25%)
    Surgery only 1 (1.0%)
PFS.Event
    No Progression 63 (65%)
    Progression 34 (35%)
OS.Event
    Alive 81 (84%)
    Deceased 16 (16%)
OS.months 22 (2 - 56)
1 n (%); Median (Min - Max)
fit1 <- as_flex_table(
  table1,
  include = everything(),
  return_calls = FALSE
)
fit1

Characteristic

N = 971

Sex

Female

17 (18%)

Male

80 (82%)

Age

67 (30 - 96)

Tobacco.History

63 (65%)

Prim.Location

Larynx/Hypopharynx

5 (5.2%)

Oral cavity

16 (16%)

Oropharynx

67 (69%)

Other (paranasal sinus and nasopharyngeal)

9 (9.3%)

cT

T0

2 (2.1%)

T1

12 (12%)

T2

31 (32%)

T3

30 (31%)

T4

21 (22%)

TX

1 (1.0%)

cN

N0

22 (23%)

N1

33 (34%)

N2

33 (34%)

N3

9 (9.3%)

cM

M0

93 (96%)

M1

4 (4.1%)

Histology

Adenosquamous carcinoma

1 (1.0%)

Basaloid squamous cell carcinoma

6 (6.2%)

Epithelial myoepithelial carcinoma

1 (1.0%)

Squamous cell carcinoma

86 (89%)

Undifferentiated carcinoma

3 (3.1%)

Stage

I/II

49 (51%)

III/IVA/IVB

45 (46%)

IVC

3 (3.1%)

p16.status

Negative

43 (44%)

Positive

54 (56%)

Treatment.Group

Definitive CRT or RT

69 (71%)

None (Declined Treatment)

1 (1.0%)

None (Hospice)

2 (2.1%)

Surgery + CRT or RT

24 (25%)

Surgery only

1 (1.0%)

PFS.Event

No Progression

63 (65%)

Progression

34 (35%)

OS.Event

Alive

81 (84%)

Deceased

16 (16%)

OS.months

22 (2 - 56)

1n (%); Median (Min - Max)

save_as_docx(fit1, path= "~/Downloads/1. CLIA HNSCC UNM Demographics Table.docx")

#Demographics Table by ctDNA at baseline

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]

circ_data_subset1 <- circ_data %>%
  select(
    Sex,
    Age,
    Tobacco.History,
    Prim.Location,
    cT,
    cN,
    cM,
    Histology,
    Stage,
    p16.status,
    Treatment.Group,
    PFS.Event,
    OS.Event,
    OS.months) %>%
  mutate(
    Sex = factor(Sex),
    Age = as.numeric(Age),
    Tobacco.History = factor(Tobacco.History),
    Prim.Location = factor(Prim.Location),
    cT = factor(cT),
    cN = factor(cN),
    cM = factor(cM),
    Histology = factor(Histology),
    Stage = factor(Stage),
    p16.status = factor(p16.status),
    Treatment.Group = factor(Treatment.Group),
    PFS.Event = factor(PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression")),
    OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
    OS.months = as.numeric(OS.months)) 

circ_data1 <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]

circ_data_subset2 <- circ_data1 %>%
  select(
    Sex,
    Age,
    Tobacco.History,
    Prim.Location,
    cT,
    cN,
    cM,
    Histology,
    Stage,
    p16.status,
    Treatment.Group,
    PFS.Event,
    OS.Event,
    OS.months,
    ctDNA.Base) %>%
  mutate(
    Sex = factor(Sex),
    Age = as.numeric(Age),
    Tobacco.History = factor(Tobacco.History),
    Prim.Location = factor(Prim.Location),
    cT = factor(cT),
    cN = factor(cN),
    cM = factor(cM),
    Histology = factor(Histology),
    Stage = factor(Stage),
    p16.status = factor(p16.status),
    Treatment.Group = factor(Treatment.Group),
    PFS.Event = factor(PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression")),
    OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
    OS.months = as.numeric(OS.months),
    ctDNA.Base = factor(ctDNA.Base, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive")))
Overall <- circ_data_subset1 %>%
  tbl_summary(
    statistic = list(
      all_continuous() ~ "{median} ({min} - {max})",
      all_categorical() ~ "{n} ({p}%)")) %>%
  bold_labels()
Overall
Characteristic N = 971
Sex
    Female 17 (18%)
    Male 80 (82%)
Age 67 (30 - 96)
Tobacco.History 63 (65%)
Prim.Location
    Larynx/Hypopharynx 5 (5.2%)
    Oral cavity 16 (16%)
    Oropharynx 67 (69%)
    Other (paranasal sinus and nasopharyngeal) 9 (9.3%)
cT
    T0 2 (2.1%)
    T1 12 (12%)
    T2 31 (32%)
    T3 30 (31%)
    T4 21 (22%)
    TX 1 (1.0%)
cN
    N0 22 (23%)
    N1 33 (34%)
    N2 33 (34%)
    N3 9 (9.3%)
cM
    M0 93 (96%)
    M1 4 (4.1%)
Histology
    Adenosquamous carcinoma 1 (1.0%)
    Basaloid squamous cell carcinoma 6 (6.2%)
    Epithelial myoepithelial carcinoma 1 (1.0%)
    Squamous cell carcinoma 86 (89%)
    Undifferentiated carcinoma 3 (3.1%)
Stage
    I/II 49 (51%)
    III/IVA/IVB 45 (46%)
    IVC 3 (3.1%)
p16.status
    Negative 43 (44%)
    Positive 54 (56%)
Treatment.Group
    Definitive CRT or RT 69 (71%)
    None (Declined Treatment) 1 (1.0%)
    None (Hospice) 2 (2.1%)
    Surgery + CRT or RT 24 (25%)
    Surgery only 1 (1.0%)
PFS.Event
    No Progression 63 (65%)
    Progression 34 (35%)
OS.Event
    Alive 81 (84%)
    Deceased 16 (16%)
OS.months 22 (2 - 56)
1 n (%); Median (Min - Max)

ByctDNA_MRD <- circ_data_subset2 %>%
  tbl_summary(
    by = ctDNA.Base, # add this line to subgroup by ctDNA.Base
    statistic = list(
      all_continuous() ~ "{median} ({min} - {max})",
      all_categorical() ~ "{n} ({p}%)")) %>%
  add_p() %>%
  bold_labels()
ByctDNA_MRD
Characteristic Negative
N = 7
1
Positive
N = 56
1
p-value2
Sex

0.10
    Female 3 (43%) 8 (14%)
    Male 4 (57%) 48 (86%)
Age 81 (54 - 96) 66 (38 - 96) 0.080
Tobacco.History 4 (57%) 38 (68%) 0.7
Prim.Location

0.066
    Larynx/Hypopharynx 0 (0%) 3 (5.4%)
    Oral cavity 3 (43%) 5 (8.9%)
    Oropharynx 3 (43%) 44 (79%)
    Other (paranasal sinus and nasopharyngeal) 1 (14%) 4 (7.1%)
cT

0.051
    T0 0 (0%) 2 (3.6%)
    T1 1 (14%) 4 (7.1%)
    T2 2 (29%) 20 (36%)
    T3 0 (0%) 21 (38%)
    T4 4 (57%) 9 (16%)
    TX 0 (0%) 0 (0%)
cN

>0.9
    N0 1 (14%) 11 (20%)
    N1 3 (43%) 19 (34%)
    N2 3 (43%) 20 (36%)
    N3 0 (0%) 6 (11%)
cM

>0.9
    M0 7 (100%) 54 (96%)
    M1 0 (0%) 2 (3.6%)
Histology

0.14
    Adenosquamous carcinoma 0 (0%) 0 (0%)
    Basaloid squamous cell carcinoma 0 (0%) 3 (5.4%)
    Epithelial myoepithelial carcinoma 0 (0%) 0 (0%)
    Squamous cell carcinoma 6 (86%) 53 (95%)
    Undifferentiated carcinoma 1 (14%) 0 (0%)
Stage

0.4
    I/II 2 (29%) 32 (57%)
    III/IVA/IVB 5 (71%) 22 (39%)
    IVC 0 (0%) 2 (3.6%)
p16.status

0.095
    Negative 5 (71%) 19 (34%)
    Positive 2 (29%) 37 (66%)
Treatment.Group

0.3
    Definitive CRT or RT 5 (71%) 51 (91%)
    None (Declined Treatment) 0 (0%) 0 (0%)
    None (Hospice) 0 (0%) 1 (1.8%)
    Surgery + CRT or RT 2 (29%) 3 (5.4%)
    Surgery only 0 (0%) 1 (1.8%)
PFS.Event

0.4
    No Progression 6 (86%) 35 (63%)
    Progression 1 (14%) 21 (38%)
OS.Event

0.3
    Alive 7 (100%) 44 (79%)
    Deceased 0 (0%) 12 (21%)
OS.months 31 (21 - 39) 16 (2 - 45) 0.026
1 n (%); Median (Min - Max)
2 Fisher’s exact test; Wilcoxon rank sum test

merged_table <- tbl_merge(tbls=list(Overall, ByctDNA_MRD))
merged_table
Characteristic
Table 1
Table 2
N = 971 Negative
N = 7
1
Positive
N = 56
1
p-value2
Sex


0.10
    Female 17 (18%) 3 (43%) 8 (14%)
    Male 80 (82%) 4 (57%) 48 (86%)
Age 67 (30 - 96) 81 (54 - 96) 66 (38 - 96) 0.080
Tobacco.History 63 (65%) 4 (57%) 38 (68%) 0.7
Prim.Location


0.066
    Larynx/Hypopharynx 5 (5.2%) 0 (0%) 3 (5.4%)
    Oral cavity 16 (16%) 3 (43%) 5 (8.9%)
    Oropharynx 67 (69%) 3 (43%) 44 (79%)
    Other (paranasal sinus and nasopharyngeal) 9 (9.3%) 1 (14%) 4 (7.1%)
cT


0.051
    T0 2 (2.1%) 0 (0%) 2 (3.6%)
    T1 12 (12%) 1 (14%) 4 (7.1%)
    T2 31 (32%) 2 (29%) 20 (36%)
    T3 30 (31%) 0 (0%) 21 (38%)
    T4 21 (22%) 4 (57%) 9 (16%)
    TX 1 (1.0%) 0 (0%) 0 (0%)
cN


>0.9
    N0 22 (23%) 1 (14%) 11 (20%)
    N1 33 (34%) 3 (43%) 19 (34%)
    N2 33 (34%) 3 (43%) 20 (36%)
    N3 9 (9.3%) 0 (0%) 6 (11%)
cM


>0.9
    M0 93 (96%) 7 (100%) 54 (96%)
    M1 4 (4.1%) 0 (0%) 2 (3.6%)
Histology


0.14
    Adenosquamous carcinoma 1 (1.0%) 0 (0%) 0 (0%)
    Basaloid squamous cell carcinoma 6 (6.2%) 0 (0%) 3 (5.4%)
    Epithelial myoepithelial carcinoma 1 (1.0%) 0 (0%) 0 (0%)
    Squamous cell carcinoma 86 (89%) 6 (86%) 53 (95%)
    Undifferentiated carcinoma 3 (3.1%) 1 (14%) 0 (0%)
Stage


0.4
    I/II 49 (51%) 2 (29%) 32 (57%)
    III/IVA/IVB 45 (46%) 5 (71%) 22 (39%)
    IVC 3 (3.1%) 0 (0%) 2 (3.6%)
p16.status


0.095
    Negative 43 (44%) 5 (71%) 19 (34%)
    Positive 54 (56%) 2 (29%) 37 (66%)
Treatment.Group


0.3
    Definitive CRT or RT 69 (71%) 5 (71%) 51 (91%)
    None (Declined Treatment) 1 (1.0%) 0 (0%) 0 (0%)
    None (Hospice) 2 (2.1%) 0 (0%) 1 (1.8%)
    Surgery + CRT or RT 24 (25%) 2 (29%) 3 (5.4%)
    Surgery only 1 (1.0%) 0 (0%) 1 (1.8%)
PFS.Event


0.4
    No Progression 63 (65%) 6 (86%) 35 (63%)
    Progression 34 (35%) 1 (14%) 21 (38%)
OS.Event


0.3
    Alive 81 (84%) 7 (100%) 44 (79%)
    Deceased 16 (16%) 0 (0%) 12 (21%)
OS.months 22 (2 - 56) 31 (21 - 39) 16 (2 - 45) 0.026
1 n (%); Median (Min - Max)
2 Fisher’s exact test; Wilcoxon rank sum test

fit1 <- as_flex_table(
  merged_table,
  include = everything(),
  return_calls = FALSE
)
fit1

Table 1

Table 2

Characteristic

N = 971

Negative
N = 71

Positive
N = 561

p-value2

Sex

0.10

Female

17 (18%)

3 (43%)

8 (14%)

Male

80 (82%)

4 (57%)

48 (86%)

Age

67 (30 - 96)

81 (54 - 96)

66 (38 - 96)

0.080

Tobacco.History

63 (65%)

4 (57%)

38 (68%)

0.7

Prim.Location

0.066

Larynx/Hypopharynx

5 (5.2%)

0 (0%)

3 (5.4%)

Oral cavity

16 (16%)

3 (43%)

5 (8.9%)

Oropharynx

67 (69%)

3 (43%)

44 (79%)

Other (paranasal sinus and nasopharyngeal)

9 (9.3%)

1 (14%)

4 (7.1%)

cT

0.051

T0

2 (2.1%)

0 (0%)

2 (3.6%)

T1

12 (12%)

1 (14%)

4 (7.1%)

T2

31 (32%)

2 (29%)

20 (36%)

T3

30 (31%)

0 (0%)

21 (38%)

T4

21 (22%)

4 (57%)

9 (16%)

TX

1 (1.0%)

0 (0%)

0 (0%)

cN

>0.9

N0

22 (23%)

1 (14%)

11 (20%)

N1

33 (34%)

3 (43%)

19 (34%)

N2

33 (34%)

3 (43%)

20 (36%)

N3

9 (9.3%)

0 (0%)

6 (11%)

cM

>0.9

M0

93 (96%)

7 (100%)

54 (96%)

M1

4 (4.1%)

0 (0%)

2 (3.6%)

Histology

0.14

Adenosquamous carcinoma

1 (1.0%)

0 (0%)

0 (0%)

Basaloid squamous cell carcinoma

6 (6.2%)

0 (0%)

3 (5.4%)

Epithelial myoepithelial carcinoma

1 (1.0%)

0 (0%)

0 (0%)

Squamous cell carcinoma

86 (89%)

6 (86%)

53 (95%)

Undifferentiated carcinoma

3 (3.1%)

1 (14%)

0 (0%)

Stage

0.4

I/II

49 (51%)

2 (29%)

32 (57%)

III/IVA/IVB

45 (46%)

5 (71%)

22 (39%)

IVC

3 (3.1%)

0 (0%)

2 (3.6%)

p16.status

0.095

Negative

43 (44%)

5 (71%)

19 (34%)

Positive

54 (56%)

2 (29%)

37 (66%)

Treatment.Group

0.3

Definitive CRT or RT

69 (71%)

5 (71%)

51 (91%)

None (Declined Treatment)

1 (1.0%)

0 (0%)

0 (0%)

None (Hospice)

2 (2.1%)

0 (0%)

1 (1.8%)

Surgery + CRT or RT

24 (25%)

2 (29%)

3 (5.4%)

Surgery only

1 (1.0%)

0 (0%)

1 (1.8%)

PFS.Event

0.4

No Progression

63 (65%)

6 (86%)

35 (63%)

Progression

34 (35%)

1 (14%)

21 (38%)

OS.Event

0.3

Alive

81 (84%)

7 (100%)

44 (79%)

Deceased

16 (16%)

0 (0%)

12 (21%)

OS.months

22 (2 - 56)

31 (21 - 39)

16 (2 - 45)

0.026

1n (%); Median (Min - Max)

2Fisher's exact test; Wilcoxon rank sum test

save_as_docx(fit1, path = "~/Downloads/1b. CLIA HNSCC UNM Demographics Table by ctDNA.docx")

#Overview plot by Stage

setwd("~/Downloads") 
clinstage <- read.csv("CLIA HNSCC UNM_OP.csv")
clinstage_df <- as.data.frame(clinstage)

# Creating the basic swimmer plot
oplot <- swimmer_plot(df=clinstage_df,
                      id='PatientName',
                      end='fu.diff.months',
                      fill='gray',
                      width=.01,
                      base_size = 14,
                      stratify= c('Stage'))

# Adding themes and scales
oplot <- oplot + theme(panel.border = element_blank())
oplot <- oplot + scale_y_continuous(breaks = seq(0, 72, by = 3))
oplot <- oplot + labs(x ="Patients", y="Months from Diagnosis")

# Adding swimmer points
oplot_ev1 <- oplot + swimmer_points(df_points=clinstage_df,
                                    id='PatientName',
                                    time='date.diff.months',
                                    name_shape ='Event_type',
                                    name_col = 'Event',
                                    size=3.5,fill='black')
# Optionally uncomment and use col='darkgreen' if needed

# Adding shape manual scale
oplot_ev1.1 <- oplot_ev1 + ggplot2::scale_shape_manual(name="Event_type",
                                                       values=c(1,16,6,18,18,4),
                                                       breaks=c('ctDNA_neg','ctDNA_pos', 'Imaging','Surgery','Biopsy', 'Death'))

# Display the plot
oplot_ev1.1

oplot_ev2 <- oplot_ev1.1 + swimmer_lines(df_lines=clinstage_df,
                                         id='PatientName',
                                         start='Tx_start.months',
                                         end='Tx_end.months',
                                         name_col='Tx_type',
                                         size=3.5,
                                         name_alpha = 1.0)
oplot_ev2 <- oplot_ev2 + guides(linetype = guide_legend(override.aes = list(size = 5, color = "black")))
oplot_ev2

oplot_ev2.2 <- oplot_ev2 + ggplot2::scale_color_manual(name="Event",values=c( "grey", "orange", "black", "black", "green", "red", "purple", "blue"))
oplot_ev2.2

#PFS in Complete Cohort (N=97)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.available, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.available, data = circ_data)

      n events median 0.95LCL 0.95UCL
[1,] 97     34   40.1    40.1      NA
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.available, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue"), title="PFS - Complete Cohort (n=97)", ylab= "Progression-Free Survival", xlab="Months from Start of definitive Treatment", legend.labs=c("Complete cohort"), legend.title="")

summary(KM_curve, times= c(12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.available, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     64      23    0.760  0.0438        0.660        0.833
   24     36       8    0.651  0.0520        0.538        0.742
   36     13       2    0.612  0.0555        0.494        0.711

#OS in Complete Cohort (N=97)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]

survfit(Surv(time = circ_data$OS.months, event = circ_data$OS.Event)~ctDNA.available, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$OS.months, event = circ_data$OS.Event) ~ 
    ctDNA.available, data = circ_data)

      n events median 0.95LCL 0.95UCL
[1,] 97     16     NA      NA      NA
surv_object <-Surv(time = circ_data$OS.months, event = circ_data$OS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.available, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue"), title="OS - Complete Cohort (n=97)", ylab= "Overall Survival", xlab="Months from Start of definitive Treatment", legend.labs=c("Complete cohort"), legend.title="")

summary(KM_curve, times= c(12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.available, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     73      13    0.866  0.0347        0.780        0.920
   24     42       3    0.822  0.0412        0.724        0.888
   36     18       0    0.822  0.0412        0.724        0.888

#Association of Baseline ctDNA MTM levels with clinicopathological factors

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cStage, data=circ_data, margins = TRUE)
cStage
  I/II III/IV  Total 
    34     29     63 
circ_data$cStage <- factor(circ_data$cStage, levels = c("I/II","III/IV"), labels = c("I/II (n=34)","III/IV (n=29)"))
boxplot(ctDNA.Base.MTM~cStage, data=circ_data, main="ctDNA pre-treatment MTM - Stage", xlab="Stage", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.Stage <- circ_data %>%
  group_by(cStage) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.Stage)
# A tibble: 2 × 2
  cStage        median_ctDNA_Base_MTM
  <fct>                         <dbl>
1 I/II (n=34)                   16.0 
2 III/IV (n=29)                  5.43
m1<-wilcox.test(ctDNA.Base.MTM ~ cStage, data=circ_data, na.rm=TRUE, exact=FALSE, conf.int=TRUE)
print(m1)

    Wilcoxon rank sum test with continuity correction

data:  ctDNA.Base.MTM by cStage
W = 601, p-value = 0.138
alternative hypothesis: true location shift is not equal to 0
95 percent confidence interval:
 -1.04003 17.95005
sample estimates:
difference in location 
              3.110541 
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cT.status, data=circ_data, margins = TRUE)
cT.status
T0-T2 T3-T4 Total 
   29    34    63 
circ_data$cT.status <- factor(circ_data$cT.status, levels = c("T0-T2","T3-T4"), labels = c("T0-T2 (n=29)","T3-T4 (n=34)"))
boxplot(ctDNA.Base.MTM~cT.status, data=circ_data, main="ctDNA pre-treatment MTM - T stage", xlab="T stage", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.cT <- circ_data %>%
  group_by(cT.status) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.cT)
# A tibble: 2 × 2
  cT.status    median_ctDNA_Base_MTM
  <fct>                        <dbl>
1 T0-T2 (n=29)                 10.0 
2 T3-T4 (n=34)                  7.15
m2<-wilcox.test(ctDNA.Base.MTM ~ cT.status, data=circ_data, na.rm=TRUE, exact=FALSE, conf.int=TRUE)
print(m2)

    Wilcoxon rank sum test with continuity correction

data:  ctDNA.Base.MTM by cT.status
W = 489, p-value = 0.9615
alternative hypothesis: true location shift is not equal to 0
95 percent confidence interval:
 -7.720014  8.029990
sample estimates:
difference in location 
         -4.450499e-05 
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cT, data=circ_data, margins = TRUE)
cT
   T0    T1    T2    T3    T4 Total 
    2     5    22    21    13    63 
circ_data$cT <- factor(circ_data$cT, levels = c("T0","T1","T2","T3","T4"))
boxplot(ctDNA.Base.MTM~cT, data=circ_data, main="ctDNA pre-treatment MTM - cT status", xlab="cT status", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.cT <- circ_data %>%
  group_by(cT) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.cT)
# A tibble: 5 × 2
  cT    median_ctDNA_Base_MTM
  <fct>                 <dbl>
1 T0                     1.82
2 T1                     7.54
3 T2                    14.1 
4 T3                    22.3 
5 T4                     5.43
pairwise_wilcox <- pairwise.wilcox.test(circ_data$ctDNA.Base.MTM, circ_data$cT, 
                                        p.adjust.method = "none", 
                                        exact = FALSE)
print(pairwise_wilcox)

    Pairwise comparisons using Wilcoxon rank sum test with continuity correction 

data:  circ_data$ctDNA.Base.MTM and circ_data$cT 

   T0   T1   T2   T3  
T1 0.85 -    -    -   
T2 0.19 0.83 -    -   
T3 0.14 0.65 0.76 -   
T4 0.55 0.62 0.33 0.23

P value adjustment method: none 
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available == "TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base != "",]
circ_data$cT <- factor(circ_data$cT, levels = c("T0", "T1", "T2", "T3", "T4"))
circ_data$ctDNA.Base.MTM <- as.numeric(circ_data$ctDNA.Base.MTM)
cT_levels <- levels(circ_data$cT)
p_value_matrix <- matrix(NA, nrow = length(cT_levels), ncol = length(cT_levels))
rownames(p_value_matrix) <- cT_levels
colnames(p_value_matrix) <- cT_levels

for (i in 1:length(cT_levels)) {
  for (j in i:length(cT_levels)) {
    if (i != j) {
      # Extract data for both groups
      data1 <- circ_data %>% filter(cT == cT_levels[i]) %>% pull(ctDNA.Base.MTM)
      data2 <- circ_data %>% filter(cT == cT_levels[j]) %>% pull(ctDNA.Base.MTM)
      
      # Perform Wilcoxon test and store p-value
      test_result <- wilcox.test(data1, data2, exact = FALSE)
      p_value_matrix[i, j] <- test_result$p.value
      p_value_matrix[j, i] <- test_result$p.value  # Make symmetric
    } else {
      p_value_matrix[i, j] <- 1  # Self-comparison = 1
    }
  }
}

p_value_matrix[is.na(p_value_matrix)] <- 1.00
p_value_data <- melt(p_value_matrix)
colnames(p_value_data) <- c("cT1", "cT2", "p_value")
p_value_data <- p_value_data %>%
  mutate(
    significance = case_when(
      p_value < 0.001 ~ "***",
      p_value < 0.01 ~ "**",
      p_value < 0.05 ~ "*",
      TRUE ~ ""
    )
  )

ggplot(p_value_data, aes(x = cT1, y = cT2, fill = p_value)) +
  geom_tile(color = "white", size = 0.8) +  # Thicker grid lines for separation
  geom_text(aes(label = significance), color = "black", size = 6, fontface = "bold") +  # Significance markers
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0.05) +  # Gradient colors
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 12, face = "bold"),
        axis.text.y = element_text(size = 12, face = "bold"),
        panel.grid = element_blank()) +
  labs(title = "Pairwise Wilcoxon-Test P-Values (ctDNA.Base.MTM by cT)",
       x = "cT Status", y = "cT Status", fill = "P-Value")


rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cN.status, data=circ_data, margins = TRUE)
cN.status
   N0 N1-N3 Total 
   12    51    63 
circ_data$cN.status <- factor(circ_data$cN.status, levels = c("N0","N1-N3"), labels = c("N0 (n=12)","N1-N3 (n=51)"))
boxplot(ctDNA.Base.MTM~cN.status, data=circ_data, main="ctDNA pre-treatment MTM - cN status", xlab="cN status", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.cN <- circ_data %>%
  group_by(cN.status) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.cN)
# A tibble: 2 × 2
  cN.status    median_ctDNA_Base_MTM
  <fct>                        <dbl>
1 N0 (n=12)                     2.06
2 N1-N3 (n=51)                 16.3 
m3<-wilcox.test(ctDNA.Base.MTM ~ cN.status, data=circ_data, na.rm=TRUE, exact=FALSE, conf.int=TRUE)
print(m3)

    Wilcoxon rank sum test with continuity correction

data:  ctDNA.Base.MTM by cN.status
W = 163, p-value = 0.01256
alternative hypothesis: true location shift is not equal to 0
95 percent confidence interval:
 -41.019963  -1.190095
sample estimates:
difference in location 
             -11.93637 
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cN, data=circ_data, margins = TRUE)
cN
   N0    N1    N2    N3 Total 
   12    22    23     6    63 
circ_data$cN <- factor(circ_data$cN, levels = c("N0","N1","N2","N3"))
boxplot(ctDNA.Base.MTM~cN, data=circ_data, main="ctDNA pre-treatment MTM - N Stage", xlab="N Stage", ylab="MTM/mL", col="white",border="black", ylim = c(0, 500))

median_ctDNA.cN <- circ_data %>%
  group_by(cN) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.cN)
# A tibble: 4 × 2
  cN    median_ctDNA_Base_MTM
  <fct>                 <dbl>
1 N0                     2.06
2 N1                    14.3 
3 N2                    32.0 
4 N3                     5.55
pairwise_wilcox <- pairwise.wilcox.test(circ_data$ctDNA.Base.MTM, circ_data$cN, 
                                        p.adjust.method = "none", 
                                        exact = FALSE)
print(pairwise_wilcox)

    Pairwise comparisons using Wilcoxon rank sum test with continuity correction 

data:  circ_data$ctDNA.Base.MTM and circ_data$cN 

   N0     N1     N2    
N1 0.0473 -      -     
N2 0.0074 0.3513 -     
N3 0.3736 0.9777 0.7262

P value adjustment method: none 
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available == "TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base != "",]
circ_data$cN <- factor(circ_data$cN, levels = c("N0","N1","N2","N3"))
circ_data$ctDNA.Base.MTM <- as.numeric(circ_data$ctDNA.Base.MTM)
cN_levels <- levels(circ_data$cN)
p_value_matrix <- matrix(NA, nrow = length(cN_levels), ncol = length(cN_levels))
rownames(p_value_matrix) <- cN_levels
colnames(p_value_matrix) <- cN_levels

for (i in 1:length(cN_levels)) {
  for (j in i:length(cN_levels)) {
    if (i != j) {
      # Extract data for both groups
      data1 <- circ_data %>% filter(cN == cN_levels[i]) %>% pull(ctDNA.Base.MTM)
      data2 <- circ_data %>% filter(cN == cN_levels[j]) %>% pull(ctDNA.Base.MTM)
      
      # Perform Wilcoxon test and store p-value
      test_result <- wilcox.test(data1, data2, exact = FALSE)
      p_value_matrix[i, j] <- test_result$p.value
      p_value_matrix[j, i] <- test_result$p.value  # Make symmetric
    } else {
      p_value_matrix[i, j] <- 1  # Self-comparison = 1
    }
  }
}

p_value_matrix[is.na(p_value_matrix)] <- 1.00
p_value_data <- melt(p_value_matrix)
colnames(p_value_data) <- c("cN1", "cN2", "p_value")
p_value_data <- p_value_data %>%
  mutate(
    significance = case_when(
      p_value < 0.001 ~ "***",
      p_value < 0.01 ~ "**",
      p_value < 0.05 ~ "*",
      TRUE ~ ""
    )
  )

ggplot(p_value_data, aes(x = cN1, y = cN2, fill = p_value)) +
  geom_tile(color = "white", size = 0.8) +  # Thicker grid lines for separation
  geom_text(aes(label = significance), color = "black", size = 6, fontface = "bold") +  # Significance markers
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0.05) +  # Gradient colors
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 12, face = "bold"),
        axis.text.y = element_text(size = 12, face = "bold"),
        panel.grid = element_blank()) +
  labs(title = "Pairwise Wilcoxon-Test P-Values (ctDNA.Base.MTM by cN)",
       x = "Status", y = "cN Status", fill = "P-Value")


rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available == "TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base != "",]
circ_data$cT <- factor(circ_data$cT, levels = c("T0", "T1", "T2", "T3", "T4"))
circ_data$cN <- factor(circ_data$cN, levels = c("N0", "N1", "N2", "N3"))
circ_data$ctDNA.Base.MTM <- as.numeric(circ_data$ctDNA.Base.MTM)

median_ctDNA <- circ_data %>%
  group_by(cT, cN) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE)) %>%
  ungroup()

p_value_matrix <- dcast(median_ctDNA, cT ~ cN, value.var = "median_ctDNA_Base_MTM")
p_value_data <- melt(p_value_matrix, id.vars = "cT", variable.name = "cN", value.name = "median_value")
p_value_data$missing <- ifelse(is.na(p_value_data$median_value), "Missing", "Present")
p_value_data$median_value[is.na(p_value_data$median_value)] <- 0

ggplot(p_value_data, aes(x = cN, y = cT, fill = median_value)) +
  geom_tile(color = "black", size = 0.5) +  # Black gridlines for separation
  geom_text(aes(label = round(median_value, 2)), color = "black", size = 5) +  # Display median values
  scale_fill_gradient(low = "white", high = "blue") +  # Color gradient similar to the reference image
  theme_minimal() +
  theme(axis.text.x = element_text(size = 12, face = "bold"),
        axis.text.y = element_text(size = 12, face = "bold"),
        panel.grid = element_blank()) +
  labs(title = "Median ctDNA.Base.MTM by cT and cN",
       x = "cN Status", y = "cT Status", fill = "Median MTM") +
  geom_tile(data = subset(p_value_data, missing == "Missing"), 
            aes(x = cN, y = cT), color = "black", fill = NA, size = 0.5, linetype = "dashed")  # Add diagonal cross for missing values


rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~cM, data=circ_data, margins = TRUE)
cM
   M0    M1 Total 
   61     2    63 
circ_data$cM <- factor(circ_data$cM, levels = c("M0","M1"), labels = c("M0 (n=61)","M1 (n=2)"))
boxplot(ctDNA.Base.MTM~cM, data=circ_data, main="ctDNA pre-treatment MTM - cM", xlab="cM", ylab="MTM/mL", col="white",border="black", ylim = c(0, 500))

median_ctDNA.cM <- circ_data %>%
  group_by(cM) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.cM)
# A tibble: 2 × 2
  cM        median_ctDNA_Base_MTM
  <fct>                     <dbl>
1 M0 (n=61)                  8.84
2 M1 (n=2)                 235.  
m4<-wilcox.test(ctDNA.Base.MTM ~ cM, data=circ_data, na.rm=TRUE, exact=FALSE, conf.int=TRUE)
print(m4)

    Wilcoxon rank sum test with continuity correction

data:  ctDNA.Base.MTM by cM
W = 54, p-value = 0.7987
alternative hypothesis: true location shift is not equal to 0
95 percent confidence interval:
 -469.97996   76.10995
sample estimates:
difference in location 
           -0.07005084 
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~p16.status, data=circ_data, margins = TRUE)
p16.status
Negative Positive    Total 
      24       39       63 
circ_data$p16.status <- factor(circ_data$p16.status, levels = c("Negative","Positive"), labels = c("p16 neg (n=24)","p16 pos (n=39)"))
boxplot(ctDNA.Base.MTM~p16.status, data=circ_data, main="ctDNA pre-treatment MTM - p16 status", xlab="p16 status", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.p16 <- circ_data %>%
  group_by(p16.status) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.p16)
# A tibble: 2 × 2
  p16.status     median_ctDNA_Base_MTM
  <fct>                          <dbl>
1 p16 neg (n=24)                  2.82
2 p16 pos (n=39)                 16.3 
m5<-wilcox.test(ctDNA.Base.MTM ~ p16.status, data=circ_data, na.rm=TRUE, exact=FALSE, conf.int=TRUE)
print(m5)

    Wilcoxon rank sum test with continuity correction

data:  ctDNA.Base.MTM by p16.status
W = 294, p-value = 0.014
alternative hypothesis: true location shift is not equal to 0
95 percent confidence interval:
 -27.0900146  -0.8600542
sample estimates:
difference in location 
             -8.507328 
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base!="",]
circ_datadf <- as.data.frame(circ_data)

tally(~Prim.Location, data=circ_data, margins = TRUE)
Prim.Location
                        Larynx/Hypopharynx                                Oral cavity                                 Oropharynx Other (paranasal sinus and nasopharyngeal) 
                                         3                                          8                                         47                                          5 
                                     Total 
                                        63 
circ_data$Prim.Location <- factor(circ_data$Prim.Location, levels = c("Larynx/Hypopharynx","Oral cavity", "Oropharynx", "Other (paranasal sinus and nasopharyngeal)"))
boxplot(ctDNA.Base.MTM~Prim.Location, data=circ_data, main="ctDNA pre-treatment MTM - Tumor Location", xlab="Tumor Location", ylab="MTM/mL", col="white",border="black", ylim = c(0, 200))

median_ctDNA.loc <- circ_data %>%
  group_by(Prim.Location) %>%
  summarise(median_ctDNA_Base_MTM = median(ctDNA.Base.MTM, na.rm = TRUE))
print(median_ctDNA.loc)
# A tibble: 4 × 2
  Prim.Location                              median_ctDNA_Base_MTM
  <fct>                                                      <dbl>
1 Larynx/Hypopharynx                                          2.08
2 Oral cavity                                                 2.81
3 Oropharynx                                                 15.8 
4 Other (paranasal sinus and nasopharyngeal)                  5.43
pairwise_wilcox <- pairwise.wilcox.test(circ_data$ctDNA.Base.MTM, circ_data$Prim.Location, 
                                        p.adjust.method = "none", 
                                        exact = FALSE)

print(pairwise_wilcox)

    Pairwise comparisons using Wilcoxon rank sum test with continuity correction 

data:  circ_data$ctDNA.Base.MTM and circ_data$Prim.Location 

                                           Larynx/Hypopharynx Oral cavity Oropharynx
Oral cavity                                0.92               -           -         
Oropharynx                                 0.25               0.12        -         
Other (paranasal sinus and nasopharyngeal) 0.55               0.77        0.65      

P value adjustment method: none 
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available == "TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Base != "",]
circ_data$Prim.Location <- factor(circ_data$Prim.Location, levels = c("Larynx/Hypopharynx","Oral cavity", "Oropharynx", "Other (paranasal sinus and nasopharyngeal)"), labels = c("LRX/HPRX","OC","PRX","Other"))
circ_data$ctDNA.Base.MTM <- as.numeric(circ_data$ctDNA.Base.MTM)
pl_levels <- levels(circ_data$Prim.Location)
p_value_matrix <- matrix(NA, nrow = length(pl_levels), ncol = length(pl_levels))
rownames(p_value_matrix) <- pl_levels
colnames(p_value_matrix) <- pl_levels

for (i in 1:length(pl_levels)) {
  for (j in i:length(pl_levels)) {
    if (i != j) {
      # Extract data for both groups
      data1 <- circ_data %>% filter(Prim.Location == pl_levels[i]) %>% pull(ctDNA.Base.MTM)
      data2 <- circ_data %>% filter(Prim.Location == pl_levels[j]) %>% pull(ctDNA.Base.MTM)
      
      # Perform Wilcoxon test and store p-value
      test_result <- wilcox.test(data1, data2, exact = FALSE)
      p_value_matrix[i, j] <- test_result$p.value
      p_value_matrix[j, i] <- test_result$p.value  # Make symmetric
    } else {
      p_value_matrix[i, j] <- 1  # Self-comparison = 1
    }
  }
}

p_value_matrix[is.na(p_value_matrix)] <- 1.00
p_value_data <- melt(p_value_matrix)
colnames(p_value_data) <- c("pl1", "pl2", "p_value")
p_value_data <- p_value_data %>%
  mutate(
    significance = case_when(
      p_value < 0.001 ~ "***",
      p_value < 0.01 ~ "**",
      p_value < 0.05 ~ "*",
      TRUE ~ ""
    )
  )

ggplot(p_value_data, aes(x = pl1, y = pl2, fill = p_value)) +
  geom_tile(color = "white", size = 0.8) +  # Thicker grid lines for separation
  geom_text(aes(label = significance), color = "black", size = 6, fontface = "bold") +  # Significance markers
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0.05) +  # Gradient colors
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 12, face = "bold"),
        axis.text.y = element_text(size = 12, face = "bold"),
        panel.grid = element_blank()) +
  labs(title = "Pairwise Wilcoxon-Test P-Values (ctDNA.Base.MTM by Tumor Location)",
       x = "Tumor Location", y = "Tumor Location", fill = "P-Value")

#PFS by ctDNA status at MRD

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 56      9     NA   40.08      NA
ctDNA.MRD=POSITIVE 13      8   15.5    4.21      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     56      9    0.161       16.1
2 POSITIVE     13      8    0.615       61.5
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     56       0    1.000  0.0000        1.000        1.000
   12     44       5    0.910  0.0384        0.797        0.962
   24     27       3    0.841  0.0523        0.705        0.918
   36      9       0    0.841  0.0523        0.705        0.918

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     13       0    1.000   0.000       1.0000        1.000
   12      6       6    0.538   0.138       0.2477        0.760
   24      2       2    0.323   0.144       0.0862        0.594
   36      2       0    0.323   0.144       0.0862        0.594
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 69, number of events= 17 

                    coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.MRDPOSITIVE 1.8545    6.3886   0.5021 3.693 0.000221 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     6.389     0.1565     2.388     17.09

Concordance= 0.69  (se = 0.059 )
Likelihood ratio test= 12.07  on 1 df,   p=5e-04
Wald test            = 13.64  on 1 df,   p=2e-04
Score (logrank) test = 17.94  on 1 df,   p=2e-05
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 6.39 (2.39-17.09); p = 0"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 9.4257, df = 1, p-value = 0.00214
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.001816
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
  1.839047 39.292099
sample estimates:
odds ratio 
  8.005348 
print(contingency_table)
          
           No Progression Progression
  Negative             47           9
  Positive              5           8
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#OS by ctDNA status at MRD

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$OS.months, event = circ_data$OS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$OS.months, event = circ_data$OS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 56      1     NA      NA      NA
ctDNA.MRD=POSITIVE 13      5     NA    12.3      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(OS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     56      1   0.0179       1.79
2 POSITIVE     13      5   0.385       38.5 
surv_object <-Surv(time = circ_data$OS.months, event = circ_data$OS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="OS - ctDNA at MRD", ylab= "Overall Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     48       1    0.982  0.0177         0.88        0.997
   24     30       0    0.982  0.0177         0.88        0.997
   36     11       0    0.982  0.0177         0.88        0.997

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12      8       3    0.769   0.117        0.442        0.919
   24      4       2    0.561   0.153        0.233        0.795
   36      3       0    0.561   0.153        0.233        0.795
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 69, number of events= 6 

                    coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.MRDPOSITIVE  3.247    25.718    1.096 2.962  0.00306 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     25.72    0.03888         3     220.5

Concordance= 0.832  (se = 0.081 )
Likelihood ratio test= 13.07  on 1 df,   p=3e-04
Wald test            = 8.77  on 1 df,   p=0.003
Score (logrank) test = 19.67  on 1 df,   p=9e-06
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 25.72 (3-220.48); p = 0.003"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$OS.Event <- factor(circ_data$OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$OS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 13.554, df = 1, p-value = 0.0002318
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.0006155
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    3.015475 1634.641331
sample estimates:
odds ratio 
  31.44433 
print(contingency_table)
          
           Alive Deceased
  Negative    55        1
  Positive     8        5
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Living Status",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("Alive" = "blue", "Deceased" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at MRD - exclude pts with adjuvant treatment post-MRD

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
excluded_ids <- c("UNM-007", "UNM-008", "UNM-023", "UNM-027", "UNM-029", 
                  "UNM-030", "UNM-035", "UNM-045", "UNM-051", "UNM-059", 
                  "UNM-075", "UNM-082", "UNM-032", "UNM-042", "UNM-043", 
                  "UNM-048", "UNM-050", "UNM-070")
circ_data <- circ_data[!circ_data$PatientID %in% excluded_ids, ]

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 44      7     NA   40.08      NA
ctDNA.MRD=POSITIVE  7      6   11.3    3.12      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     44      7    0.159       15.9
2 POSITIVE      7      6    0.857       85.7
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     44       0    1.000  0.0000        1.000        1.000
   12     37       3    0.932  0.0380        0.803        0.977
   24     21       3    0.847  0.0582        0.688        0.929
   36      8       0    0.847  0.0582        0.688        0.929

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      7       0    1.000   0.000      1.00000        1.000
   12      3       4    0.429   0.187      0.09775        0.734
   24      1       2    0.143   0.132      0.00712        0.465
   36      1       0    0.143   0.132      0.00712        0.465
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 51, number of events= 13 

                     coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.MRDPOSITIVE  2.3109   10.0834   0.5805 3.981 6.87e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     10.08    0.09917     3.232     31.46

Concordance= 0.71  (se = 0.067 )
Likelihood ratio test= 13.27  on 1 df,   p=3e-04
Wald test            = 15.85  on 1 df,   p=7e-05
Score (logrank) test = 24.07  on 1 df,   p=9e-07
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 10.08 (3.23-31.46); p = 0"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 12.037, df = 1, p-value = 0.0005216
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.0005781
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    2.866206 1480.826861
sample estimates:
odds ratio 
  28.65583 
print(contingency_table)
          
           No Progression Progression
  Negative             37           7
  Positive              1           6
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#OS by ctDNA status at MRD - exclude pts with adjuvant treatment post-MRD

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
excluded_ids <- c("UNM-007", "UNM-008", "UNM-023", "UNM-027", "UNM-029", 
                  "UNM-030", "UNM-035", "UNM-045", "UNM-051", "UNM-059", 
                  "UNM-075", "UNM-082", "UNM-032", "UNM-042", "UNM-043", 
                  "UNM-048", "UNM-050", "UNM-070")
circ_data <- circ_data[!circ_data$PatientID %in% excluded_ids, ]

survfit(Surv(time = circ_data$OS.months, event = circ_data$OS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$OS.months, event = circ_data$OS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 44      1     NA      NA      NA
ctDNA.MRD=POSITIVE  7      3     NA    12.3      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(OS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     44      1   0.0227       2.27
2 POSITIVE      7      3   0.429       42.9 
surv_object <-Surv(time = circ_data$OS.months, event = circ_data$OS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="OS - ctDNA at MRD", ylab= "Overall Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12     39       1    0.977  0.0225        0.849        0.997
   24     23       0    0.977  0.0225        0.849        0.997
   36      9       0    0.977  0.0225        0.849        0.997

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   12      5       1    0.857   0.132        0.334        0.979
   24      3       2    0.514   0.204        0.118        0.813
   36      2       0    0.514   0.204        0.118        0.813
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 51, number of events= 4 

                    coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.MRDPOSITIVE  3.026    20.613    1.155 2.619  0.00881 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     20.61    0.04851     2.142     198.4

Concordance= 0.799  (se = 0.119 )
Likelihood ratio test= 8.14  on 1 df,   p=0.004
Wald test            = 6.86  on 1 df,   p=0.009
Score (logrank) test = 13.95  on 1 df,   p=2e-04
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 20.61 (2.14-198.38); p = 0.009"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$OS.Event <- factor(circ_data$OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$OS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 8.7198, df = 1, p-value = 0.003148
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.006303
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    1.805221 1704.546058
sample estimates:
odds ratio 
  27.80596 
print(contingency_table)
          
           Alive Deceased
  Negative    43        1
  Positive     4        3
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Living Status",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("Alive" = "blue", "Deceased" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at MRD Stage I/II

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$cStage=="I/II",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 29      3   40.1      NA      NA
ctDNA.MRD=POSITIVE  5      2     NA    6.01      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     29      3    0.103       10.3
2 POSITIVE      5      2    0.4         40  
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD Stage I/II", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     29       0    1.000  0.0000        1.000        1.000
   12     25       1    0.966  0.0339        0.779        0.995
   24     16       1    0.925  0.0510        0.732        0.981
   36      4       0    0.925  0.0510        0.732        0.981

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      5       0      1.0   0.000        1.000        1.000
   12      2       2      0.6   0.219        0.126        0.882
   24      1       0      0.6   0.219        0.126        0.882
   36      1       0      0.6   0.219        0.126        0.882
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 34, number of events= 5 

                   coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.MRDPOSITIVE 2.286     9.838    1.035 2.209   0.0272 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     9.838     0.1016     1.294     74.78

Concordance= 0.725  (se = 0.118 )
Likelihood ratio test= 4.21  on 1 df,   p=0.04
Wald test            = 4.88  on 1 df,   p=0.03
Score (logrank) test = 7.19  on 1 df,   p=0.007
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 9.84 (1.29-74.78); p = 0.027"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 1.0932, df = 1, p-value = 0.2958
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.1464
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
  0.326544 73.672748
sample estimates:
odds ratio 
  5.350976 
print(contingency_table)
          
           No Progression Progression
  Negative             26           3
  Positive              3           2
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD Stage I/II", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at MRD Stage III/IV

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$cStage=="III/IV",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 27      6     NA      NA      NA
ctDNA.MRD=POSITIVE  8      6   13.4    4.21      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     27      6    0.222       22.2
2 POSITIVE      8      6    0.75        75  
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD Stage III/IV", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     27       0    1.000  0.0000        1.000        1.000
   12     19       4    0.850  0.0691        0.649        0.941
   24     11       2    0.753  0.0892        0.526        0.882
   36      5       0    0.753  0.0892        0.526        0.882

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      8       0     1.00   0.000       1.0000        1.000
   12      4       4     0.50   0.177       0.1520        0.775
   24      1       2     0.25   0.153       0.0371        0.558
   36      1       0     0.25   0.153       0.0371        0.558
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 35, number of events= 12 

                    coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.MRDPOSITIVE 1.4899    4.4365   0.5788 2.574     0.01 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     4.437     0.2254     1.427     13.79

Concordance= 0.663  (se = 0.07 )
Likelihood ratio test= 6.1  on 1 df,   p=0.01
Wald test            = 6.63  on 1 df,   p=0.01
Score (logrank) test = 7.93  on 1 df,   p=0.005
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 4.44 (1.43-13.79); p = 0.01"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 5.4671, df = 1, p-value = 0.01938
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.01073
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
   1.308188 121.976548
sample estimates:
odds ratio 
  9.642373 
print(contingency_table)
          
           No Progression Progression
  Negative             21           6
  Positive              2           6
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD Stage I/II", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA at MRD p16(+)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$p16.status=="Positive",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 29      2     NA      NA      NA
ctDNA.MRD=POSITIVE  8      4   22.2    6.01      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     29      2   0.0690       6.90
2 POSITIVE      8      4   0.5         50   
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD p16(+)", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     29       0    1.000  0.0000        1.000        1.000
   12     23       1    0.966  0.0339        0.779        0.995
   24     15       1    0.920  0.0553        0.711        0.980
   36      4       0    0.920  0.0553        0.711        0.980

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      8       0    1.000   0.000        1.000        1.000
   12      4       3    0.625   0.171        0.229        0.861
   24      2       1    0.417   0.205        0.072        0.747
   36      2       0    0.417   0.205        0.072        0.747
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 37, number of events= 6 

                     coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.MRDPOSITIVE  2.3218   10.1936   0.8706 2.667  0.00766 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     10.19     0.0981      1.85     56.16

Concordance= 0.767  (se = 0.09 )
Likelihood ratio test= 7.47  on 1 df,   p=0.006
Wald test            = 7.11  on 1 df,   p=0.008
Score (logrank) test = 10.81  on 1 df,   p=0.001
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 10.19 (1.85-56.16); p = 0.008"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 5.6953, df = 1, p-value = 0.01701
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.01294
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
   1.281882 176.017338
sample estimates:
odds ratio 
  12.07276 
print(contingency_table)
          
           No Progression Progression
  Negative             27           2
  Positive              4           4
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD p16(+)", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA at MRD p16(-)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$p16.status=="Negative",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 27      7   40.1   40.08      NA
ctDNA.MRD=POSITIVE  5      4   11.3    4.21      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     27      7    0.259       25.9
2 POSITIVE      5      4    0.8         80  
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at MRD p16(-)", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     27       0    1.000  0.0000        1.000        1.000
   12     21       4    0.850  0.0691        0.649        0.941
   24     12       2    0.762  0.0858        0.542        0.886
   36      5       0    0.762  0.0858        0.542        0.886

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0      5       0      1.0   0.000        1.000        1.000
   12      2       3      0.4   0.219        0.052        0.753
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 32, number of events= 11 

                    coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.MRDPOSITIVE 1.7286    5.6328   0.6515 2.653  0.00797 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     5.633     0.1775     1.571      20.2

Concordance= 0.655  (se = 0.072 )
Likelihood ratio test= 5.79  on 1 df,   p=0.02
Wald test            = 7.04  on 1 df,   p=0.008
Score (logrank) test = 8.93  on 1 df,   p=0.003
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 5.63 (1.57-20.2); p = 0.008"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 3.3339, df = 1, p-value = 0.06787
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.03671
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
   0.8530265 587.6303377
sample estimates:
odds ratio 
  10.45501 
print(contingency_table)
          
           No Progression Progression
  Negative             20           7
  Positive              1           4
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD p16(-)", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at surveillance

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 55      2     NA      NA      NA
ctDNA.Surveillance=POSITIVE 24     22     12    8.77    20.5
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              55      2   0.0364       3.64
2 POSITIVE              24     22   0.917       91.7 
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     55       0    1.000  0.0000        1.000        1.000
   12     44       2    0.964  0.0252        0.862        0.991
   24     25       0    0.964  0.0252        0.862        0.991
   36      9       0    0.964  0.0252        0.862        0.991

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     24       0    1.000  0.0000       1.0000        1.000
   12     12      12    0.500  0.1021       0.2910        0.678
   24      5       7    0.208  0.0829       0.0759        0.385
   36      2       2    0.125  0.0675       0.0314        0.287
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 79, number of events= 24 

                              coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.SurveillancePOSITIVE  3.6602   38.8674   0.7404 4.944 7.67e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     38.87    0.02573     9.107     165.9

Concordance= 0.836  (se = 0.038 )
Likelihood ratio test= 53.02  on 1 df,   p=3e-13
Wald test            = 24.44  on 1 df,   p=8e-07
Score (logrank) test = 65.38  on 1 df,   p=6e-16
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 38.87 (9.11-165.88); p = 0"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 57.128, df = 1, p-value = 4.083e-14
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 3.621e-15
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
   31.4353 3628.7424
sample estimates:
odds ratio 
  229.7243 
print(contingency_table)
          
           No Progression Progression
  Negative             53           2
  Positive              2          22
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#OS by ctDNA status at surveillance

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$OS.months, event = circ_data$OS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$OS.months, event = circ_data$OS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 55      1     NA      NA      NA
ctDNA.Surveillance=POSITIVE 24      6     NA      NA      NA
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(OS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              55      1   0.0182       1.82
2 POSITIVE              24      6   0.25        25   
surv_object <-Surv(time = circ_data$OS.months, event = circ_data$OS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="OS - ctDNA at Surveillance", ylab= "Overall Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     55       0    1.000   0.000        1.000        1.000
   12     45       1    0.982   0.018        0.878        0.997
   24     25       0    0.982   0.018        0.878        0.997
   36      9       0    0.982   0.018        0.878        0.997

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     24       0    1.000  0.0000        1.000        1.000
   12     20       3    0.875  0.0675        0.661        0.958
   24     11       3    0.715  0.1015        0.464        0.864
   36      7       0    0.715  0.1015        0.464        0.864
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 79, number of events= 7 

                             coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.SurveillancePOSITIVE  2.665    14.369    1.080 2.467   0.0136 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     14.37     0.0696     1.729     119.4

Concordance= 0.776  (se = 0.075 )
Likelihood ratio test= 9.62  on 1 df,   p=0.002
Wald test            = 6.09  on 1 df,   p=0.01
Score (logrank) test = 10.65  on 1 df,   p=0.001
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 14.37 (1.73-119.39); p = 0.014"

#Median numbers of time points in the longitudinal setting

# Load the dataset
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

median_Nsurvtps <- median(circ_datadf$Nsurvtps, na.rm = TRUE)
min_Nsurvtps <- min(circ_datadf$Nsurvtps, na.rm = TRUE)
max_Nsurvtps <- max(circ_datadf$Nsurvtps, na.rm = TRUE)

cat(sprintf("Median # of surveillance time points: %d (%d-%d)\n", 
            median_Nsurvtps, min_Nsurvtps, max_Nsurvtps))
Median # of surveillance time points: 4 (1-20)

#Time-dependent analysis for PFS in longitudinal time points

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("CLIA HNSCC Peddada Clinical Data_Time dependent.xlsx") |>
  clean_names() |>
  mutate(across(.cols = c(window_start_date,dfs_date,
                          surveillance_1_date:surveillance_13_date), 
                .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))))

dt_biomarker <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date,
         surveillance_1_status:surveillance_13_date) |>
  filter(ct_dna_surveillance_available) |>
  pivot_longer(cols = surveillance_1_status:surveillance_13_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 332
Columns: 3
$ pts_id           <chr> "UNM-002", "UNM-003", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-0…
$ biomarker_time   <dbl> 2, 14, 17, 108, 197, 240, 269, 15, 55, 168, 244, 326, 412, 46, 136, 225, 313, 417, 508, 597, 35, 246, 112, 202, 293, 386, 477, 571, 29, 99, 186, 267,…
$ biomarker_status <chr> "NEGATIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "POSITIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", NA, "…
dt_survival <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date:dfs_date, dfs_event) |>  # Added dfs_event here
  filter(ct_dna_surveillance_available) |>
  mutate(dfs_time = (dfs_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         dfs_event = as.numeric(dfs_event)) |>
  select(pts_id, dfs_time, dfs_event)

glimpse(dt_survival)
Rows: 79
Columns: 3
$ pts_id    <chr> "UNM-002", "UNM-003", "UNM-004", "UNM-008", "UNM-009", "UNM-014", "UNM-016", "UNM-017", "UNM-018", "UNM-019", "UNM-020", "UNM-021", "UNM-022", "UNM-023", "U…
$ dfs_time  <dbl> 977, 53, 398, 1043, 603, 298, 745, 402, 1148, 1155, 987, 170, 1016, 237, 535, 970, 728, 934, 1324, 989, 437, 203, 656, 647, 625, 48, 1033, 1159, 108, 591, 7…
$ dfs_event <dbl> 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, …
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, dfs_date, dfs_time)

datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_14_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = window_start_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) 

datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_14_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_14_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_14_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |> 
  group_by(n_biomarker_after_event, total_biomarker) |>  # Direct grouping
  summarise(freq = n(), .groups = "drop")  # Drop groups after summarization


tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status, 
    data = dt_final)

  n= 327, number of events= 24 
   (74 observations deleted due to missingness)

                            coef exp(coef) se(coef)    z Pr(>|z|)    
biomarker_statusPOSITIVE  4.5321   92.9506   0.7454 6.08  1.2e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     92.95    0.01076     21.57     400.6

Concordance= 0.896  (se = 0.036 )
Likelihood ratio test= 80.94  on 1 df,   p=<2e-16
Wald test            = 36.97  on 1 df,   p=1e-09
Score (logrank) test = 152.1  on 1 df,   p=<2e-16
cox_fit_summary <- summary(fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 92.95 (21.57-400.63); p = 0"

#Time-dependent analysis for OS in longitudinal time points

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("CLIA HNSCC Peddada Clinical Data_Time dependent.xlsx") |>
  clean_names() |>
  mutate(across(.cols = c(window_start_date,os_date,
                          surveillance_1_date:surveillance_13_date), 
                .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))))

dt_biomarker <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date,
         surveillance_1_status:surveillance_13_date) |>
  filter(ct_dna_surveillance_available) |>
  pivot_longer(cols = surveillance_1_status:surveillance_13_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 332
Columns: 3
$ pts_id           <chr> "UNM-002", "UNM-003", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-0…
$ biomarker_time   <dbl> 2, 14, 17, 108, 197, 240, 269, 15, 55, 168, 244, 326, 412, 46, 136, 225, 313, 417, 508, 597, 35, 246, 112, 202, 293, 386, 477, 571, 29, 99, 186, 267,…
$ biomarker_status <chr> "NEGATIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "POSITIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", NA, "…
dt_survival <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date:os_date, os_event) |>  # Added os_event here
  filter(ct_dna_surveillance_available) |>
  mutate(dfs_time = (os_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         os_event = as.numeric(os_event)) |>
  select(pts_id, dfs_time, os_event)

glimpse(dt_survival)
Rows: 79
Columns: 3
$ pts_id   <chr> "UNM-002", "UNM-003", "UNM-004", "UNM-008", "UNM-009", "UNM-014", "UNM-016", "UNM-017", "UNM-018", "UNM-019", "UNM-020", "UNM-021", "UNM-022", "UNM-023", "UN…
$ dfs_time <dbl> 977, 53, 360, 1043, 603, 298, 745, 948, 1148, 1155, 987, 423, 1016, 1093, 535, 970, 728, 934, 1324, 989, 437, 987, 1603, 647, 1108, 1346, 1033, 1159, 336, 59…
$ os_event <dbl> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1…
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, os_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = os_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, os_date, dfs_time)

datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_14_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = window_start_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) 

datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_14_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_14_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_14_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |> 
  group_by(n_biomarker_after_event, total_biomarker) |>  # Direct grouping
  summarise(freq = n(), .groups = "drop")  # Drop groups after summarization


tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              os_event = event(dfs_time, os_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, os_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, os_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, os_event) ~ biomarker_status, 
    data = dt_final)

  n= 328, number of events= 7 
   (74 observations deleted due to missingness)

                           coef exp(coef) se(coef)     z Pr(>|z|)   
biomarker_statusPOSITIVE  3.251    25.812    1.084 2.999  0.00271 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     25.81    0.03874     3.085       216

Concordance= 0.821  (se = 0.081 )
Likelihood ratio test= 14.52  on 1 df,   p=1e-04
Wald test            = 9  on 1 df,   p=0.003
Score (logrank) test = 19.82  on 1 df,   p=9e-06
cox_fit_summary <- summary(fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 25.81 (3.08-215.98); p = 0.003"

#PFS by ctDNA status at surveillance Stage I/II

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$cStage=="I/II",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 32      0     NA      NA      NA
ctDNA.Surveillance=POSITIVE 10      9   10.7    6.01      NA
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              32      0      0            0
2 POSITIVE              10      9      0.9         90
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = TRUE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance Stage I/II", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     32       0        1       0            1            1
   12     26       0        1       0            1            1
   24     15       0        1       0            1            1
   36      5       0        1       0            1            1

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     10       0      1.0   0.000       1.0000        1.000
   12      5       5      0.5   0.158       0.1836        0.753
   24      3       2      0.3   0.145       0.0711        0.578
   36      1       1      0.2   0.126       0.0309        0.475
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxphf(surv_object ~ ctDNA.Surveillance, data=circ_data) 
summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                               coef se(coef) exp(coef) lower 0.95 upper 0.95    Chisq           p
ctDNA.SurveillancePOSITIVE 4.386253 1.541238  80.33884   10.04137   10388.64 26.61378 2.48465e-07

Likelihood ratio test=26.61378 on 1 df, p=2.48465e-07, n=42
Wald test = 8.099306 on 1 df, p = 0.004428221

Covariance-Matrix:
                           ctDNA.SurveillancePOSITIVE
ctDNA.SurveillancePOSITIVE                   2.375416
cox_fit_summary <- summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                               coef se(coef) exp(coef) lower 0.95 upper 0.95    Chisq           p
ctDNA.SurveillancePOSITIVE 4.386253 1.541238  80.33884   10.04137   10388.64 26.61378 2.48465e-07

Likelihood ratio test=26.61378 on 1 df, p=2.48465e-07, n=42
Wald test = 8.099306 on 1 df, p = 0.004428221

Covariance-Matrix:
                           ctDNA.SurveillancePOSITIVE
ctDNA.SurveillancePOSITIVE                   2.375416
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 31.504, df = 1, p-value = 1.99e-08
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 2.243e-08
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
 20.64708      Inf
sample estimates:
odds ratio 
       Inf 
print(contingency_table)
          
           No Progression Progression
  Negative             32           0
  Positive              1           9
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance Stage I/II", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at surveillance Stage III/IV

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$cStage=="III/IV",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 23      2     NA      NA      NA
ctDNA.Surveillance=POSITIVE 14     13     12    10.4    24.8
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              23      2   0.0870       8.70
2 POSITIVE              14     13   0.929       92.9 
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance Stage III/IV", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     23       0    1.000  0.0000        1.000        1.000
   12     18       2    0.913  0.0588        0.695        0.978
   24     10       0    0.913  0.0588        0.695        0.978
   36      4       0    0.913  0.0588        0.695        0.978

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     14       0   1.0000  0.0000      1.00000        1.000
   12      7       7   0.5000  0.1336      0.22859        0.722
   24      2       5   0.1429  0.0935      0.02322        0.366
   36      1       1   0.0714  0.0688      0.00452        0.275
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 37, number of events= 15 

                              coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.SurveillancePOSITIVE  2.7831   16.1695   0.7642 3.642 0.000271 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     16.17    0.06184     3.616     72.31

Concordance= 0.77  (se = 0.059 )
Likelihood ratio test= 21.57  on 1 df,   p=3e-06
Wald test            = 13.26  on 1 df,   p=3e-04
Score (logrank) test = 23.62  on 1 df,   p=1e-06
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 16.17 (3.62-72.31); p = 0"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 22.2, df = 1, p-value = 2.457e-06
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 3.807e-07
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    9.097798 5733.101682
sample estimates:
odds ratio 
  101.4375 
print(contingency_table)
          
           No Progression Progression
  Negative             21           2
  Positive              1          13
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance Stage III/IV", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at surveillance p16(+)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$p16.status=="Positive",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 32      0     NA      NA      NA
ctDNA.Surveillance=POSITIVE 10      9   10.7    3.12      NA
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              32      0      0            0
2 POSITIVE              10      9      0.9         90
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = TRUE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance p16(+)", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     32       0        1       0            1            1
   12     24       0        1       0            1            1
   24     14       0        1       0            1            1
   36      5       0        1       0            1            1

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     10       0      1.0   0.000       1.0000        1.000
   12      5       5      0.5   0.158       0.1836        0.753
   24      2       3      0.2   0.126       0.0309        0.475
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxphf(surv_object ~ ctDNA.Surveillance, data=circ_data) 
summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                               coef se(coef) exp(coef) lower 0.95 upper 0.95   Chisq            p
ctDNA.SurveillancePOSITIVE 4.506786 1.534426  90.63007   11.43341   11701.57 29.1584 6.669606e-08

Likelihood ratio test=29.1584 on 1 df, p=6.669606e-08, n=42
Wald test = 8.626644 on 1 df, p = 0.003312813

Covariance-Matrix:
                           ctDNA.SurveillancePOSITIVE
ctDNA.SurveillancePOSITIVE                   2.354464
cox_fit_summary <- summary(cox_fit)
coxphf(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

Model fitted by Penalized ML
Confidence intervals and p-values by Profile Likelihood 

                               coef se(coef) exp(coef) lower 0.95 upper 0.95   Chisq            p
ctDNA.SurveillancePOSITIVE 4.506786 1.534426  90.63007   11.43341   11701.57 29.1584 6.669606e-08

Likelihood ratio test=29.1584 on 1 df, p=6.669606e-08, n=42
Wald test = 8.626644 on 1 df, p = 0.003312813

Covariance-Matrix:
                           ctDNA.SurveillancePOSITIVE
ctDNA.SurveillancePOSITIVE                   2.354464
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 31.504, df = 1, p-value = 1.99e-08
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 2.243e-08
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
 20.64708      Inf
sample estimates:
odds ratio 
       Inf 
print(contingency_table)
          
           No Progression Progression
  Negative             32           0
  Positive              1           9
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance p16(+)", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#PFS by ctDNA status at surveillance p16(-)

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$p16.status=="Negative",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 23      2     NA      NA      NA
ctDNA.Surveillance=POSITIVE 14     13     12    10.4      NA
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              23      2   0.0870       8.70
2 POSITIVE              14     13   0.929       92.9 
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance p16(-)", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     23       0    1.000  0.0000        1.000        1.000
   12     20       2    0.913  0.0588        0.695        0.978
   24     11       0    0.913  0.0588        0.695        0.978
   36      4       0    0.913  0.0588        0.695        0.978

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     14       0    1.000  0.0000       1.0000        1.000
   12      7       7    0.500  0.1336       0.2286        0.722
   24      3       4    0.214  0.1097       0.0521        0.448
   36      2       1    0.143  0.0935       0.0232        0.366
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 37, number of events= 15 

                             coef exp(coef) se(coef)     z Pr(>|z|)    
ctDNA.SurveillancePOSITIVE  2.743    15.528    0.763 3.595 0.000325 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     15.53     0.0644     3.481     69.27

Concordance= 0.769  (se = 0.061 )
Likelihood ratio test= 21  on 1 df,   p=5e-06
Wald test            = 12.92  on 1 df,   p=3e-04
Score (logrank) test = 22.81  on 1 df,   p=2e-06
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 15.53 (3.48-69.27); p = 0"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 22.2, df = 1, p-value = 2.457e-06
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 3.807e-07
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    9.097798 5733.101682
sample estimates:
odds ratio 
  101.4375 
print(contingency_table)
          
           No Progression Progression
  Negative             21           2
  Positive              1          13
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance p16(-)", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#Multivariate cox regression for PFS ctDNA status at surveillance

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.available=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]

circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$cStage <- factor(circ_data$cStage, levels = c("I/II", "III/IV"))
circ_data$p16.status <- factor(circ_data$p16.status, levels = c("Negative", "Positive"))
circ_data$Prim.Location <- factor(circ_data$Prim.Location, levels = c("Oropharynx", "Larynx/Hypopharynx", "Oral cavity", "Other (paranasal sinus and nasopharyngeal)"))
surv_object <- Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) 
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance + cStage + p16.status + Prim.Location, data=circ_data) 
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for PFS", refLabel = "Reference Group")

test.ph <- cox.zph(cox_fit)

#ctDNA and MTM/mL Dynamics for pts at surveillance window

#Dynamics and MTM/mL plots for patients with ctDNA negative at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA HNSCC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$ctDNA.Surveillance=="NEGATIVE",]

df$PFS.Event <- ifelse(df$PFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
                       ifelse(df$PFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$PFS.Event <- factor(df$PFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
  group_by(PatientName) %>%
  filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
  ungroup()

num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 55 
df_patient_pfs <- df %>%
  group_by(PatientName) %>%
  dplyr::summarize(
    PFS_True = any(PFS.Event == TRUE, na.rm = TRUE),
    PFS_False = all(PFS.Event == FALSE, na.rm = TRUE)
  )

num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)

cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 2 
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 53 
p <- ggplot(df, aes(x = date.diff.months, 
                    y = MTM.mL, 
                    group = PatientName, 
                    color = PFS.Event)) +
  geom_line() +      # Connect timepoints for each patient
  geom_point() +     # Add points for each timepoint
  # Use a log10 scale for the y-axis with specified breaks
  scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000),
                labels = c("0.01","0.1", "1", "10", "100", "1000")) +
  scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
  scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
  labs(
    x = "Time Since Surgery or start of definitive treatment (months)",
    y = "Mean Tumor Molecules per mL (MTM/mL)",
    color = "PFS Event"
  ) +
  theme_minimal()
print(p)


#Dynamics and MTM/mL plots for patients with ctDNA positive at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA HNSCC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$ctDNA.Surveillance=="POSITIVE",]

df$PFS.Event <- ifelse(df$PFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
                       ifelse(df$PFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$PFS.Event <- factor(df$PFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
  group_by(PatientName) %>%
  filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
  ungroup()

num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 23 
df_patient_pfs <- df %>%
  group_by(PatientName) %>%
  dplyr::summarize(
    PFS_True = any(PFS.Event == TRUE, na.rm = TRUE),
    PFS_False = all(PFS.Event == FALSE, na.rm = TRUE)
  )

num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)

cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 21 
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 2 
p <- ggplot(df, aes(x = date.diff.months, 
                    y = MTM.mL, 
                    group = PatientName, 
                    color = PFS.Event)) +
  geom_line() +      # Connect timepoints for each patient
  geom_point() +     # Add points for each timepoint
  # Use a log10 scale for the y-axis with specified breaks
  scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000, 10000),
                labels = c("0.01","0.1", "1", "10", "100", "1000", "10000")) +
  scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
  scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
  labs(
    x = "Time Since Surgery or start of definitive treatment (months)",
    y = "Mean Tumor Molecules per mL (MTM/mL)",
    color = "PFS Event"
  ) +
  theme_minimal()
print(p)

#PFS by ctDNA status at surveillance for pts with MRD & Surveillance time points available

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.complete=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

survfit(Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 47      1     NA      NA      NA
ctDNA.Surveillance=POSITIVE 17     15   11.3    8.18    22.2
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(PFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              47      1   0.0213       2.13
2 POSITIVE              17     15   0.882       88.2 
surv_object <-Surv(time = circ_data$PFS.months, event = circ_data$PFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="PFS - ctDNA at Surveillance", ylab= "Progression-Free Survival", xlab="Months from Definitive Treatment", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24, 36))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     47       0    1.000   0.000        1.000        1.000
   12     39       1    0.979   0.021        0.858        0.997
   24     23       0    0.979   0.021        0.858        0.997
   36      7       0    0.979   0.021        0.858        0.997

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     17       0    1.000  0.0000       1.0000        1.000
   12      8       9    0.471  0.1211       0.2296        0.680
   24      3       5    0.176  0.0925       0.0435        0.383
   36      2       0    0.176  0.0925       0.0435        0.383
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 64, number of events= 16 

                             coef exp(coef) se(coef) z Pr(>|z|)    
ctDNA.SurveillancePOSITIVE  4.141    62.867    1.035 4 6.34e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     62.87    0.01591     8.263     478.3

Concordance= 0.869  (se = 0.039 )
Likelihood ratio test= 41.58  on 1 df,   p=1e-10
Wald test            = 16  on 1 df,   p=6e-05
Score (logrank) test = 54.15  on 1 df,   p=2e-13
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 62.87 (8.26-478.32); p = 0"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$PFS.Event <- factor(circ_data$PFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Progression", "Progression"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$PFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 44.883, df = 1, p-value = 2.092e-11
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 1.312e-11
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
    24.1523 12883.6921
sample estimates:
odds ratio 
  254.5638 
print(contingency_table)
          
           No Progression Progression
  Negative             46           1
  Positive              2          15
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Progression",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Progression" = "blue", "Progression" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Progression label size

#Time-dependent analysis for PFS in longitudinal time points for pts with MRD & Surveillance time points available

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("CLIA HNSCC Peddada Clinical Data_Time dependent.xlsx") |>
  clean_names() |>
  mutate(across(.cols = c(window_start_date,dfs_date,
                          surveillance_1_date:surveillance_14_date), 
                .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))))

dt_biomarker <- dt |>
  select(pts_id, ct_dna_complete,
         window_start_date,
         surveillance_1_status:surveillance_14_date) |>
  filter(ct_dna_complete) |>
  pivot_longer(cols = surveillance_1_status:surveillance_14_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 285
Columns: 3
$ pts_id           <chr> "UNM-002", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-004", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-008", "UNM-009", "UNM-0…
$ biomarker_time   <dbl> 2, 17, 108, 197, 240, 269, 15, 55, 168, 244, 326, 412, 46, 136, 225, 313, 417, 508, 597, 35, 246, 112, 202, 293, 386, 477, 571, 3, 39, 82, 109, 195, …
$ biomarker_status <chr> "NEGATIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "POSITIVE", "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", NA, "NEGATIVE", "…
dt_survival <- dt |>
  select(pts_id, ct_dna_complete,
         window_start_date:dfs_date, dfs_event) |>  # Added dfs_event here
  filter(ct_dna_complete) |>
  mutate(dfs_time = (dfs_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         dfs_event = as.numeric(dfs_event)) |>
  select(pts_id, dfs_time, dfs_event)

glimpse(dt_survival)
Rows: 64
Columns: 3
$ pts_id    <chr> "UNM-002", "UNM-004", "UNM-008", "UNM-009", "UNM-014", "UNM-016", "UNM-019", "UNM-020", "UNM-022", "UNM-023", "UNM-024", "UNM-025", "UNM-026", "UNM-027", "U…
$ dfs_time  <dbl> 977, 398, 1043, 603, 298, 745, 1155, 987, 1016, 237, 535, 970, 728, 934, 989, 437, 203, 647, 625, 48, 1033, 1159, 591, 70, 305, 373, 126, 918, 542, 282, 501…
$ dfs_event <dbl> 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, …
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, dfs_date, dfs_time)

datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_complete,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_14_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = window_start_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) 

datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_complete,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_14_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_14_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_14_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_14_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |> 
  group_by(n_biomarker_after_event, total_biomarker) |>  # Direct grouping
  summarise(freq = n(), .groups = "drop")  # Drop groups after summarization


tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_14_date) |>
  mutate(across(.cols = dfs_date:surveillance_14_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status, 
    data = dt_final)

  n= 281, number of events= 16 
   (59 observations deleted due to missingness)

                            coef exp(coef) se(coef)     z Pr(>|z|)    
biomarker_statusPOSITIVE   4.991   147.056    1.040 4.801 1.58e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     147.1     0.0068     19.17      1128

Concordance= 0.922  (se = 0.037 )
Likelihood ratio test= 60.06  on 1 df,   p=9e-15
Wald test            = 23.05  on 1 df,   p=2e-06
Score (logrank) test = 125.2  on 1 df,   p=<2e-16
cox_fit_summary <- summary(fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 147.06 (19.17-1128.2); p = 0"

#Median numbers of time points and lead time in the longitudinal setting for pts with MRD & Surveillance time points available

# Load the dataset
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("CLIA HNSCC Peddada Clinical Data.csv")
circ_data <- circ_data[circ_data$ctDNA.complete=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

median_Nsurvtps <- median(circ_datadf$Nsurvtps, na.rm = TRUE)
min_Nsurvtps <- min(circ_datadf$Nsurvtps, na.rm = TRUE)
max_Nsurvtps <- max(circ_datadf$Nsurvtps, na.rm = TRUE)

cat(sprintf("Median # of surveillance time points: %d (%d-%d)\n", 
            median_Nsurvtps, min_Nsurvtps, max_Nsurvtps))
Median # of surveillance time points: 4 (1-20)

#Median of median interval of ctDNA timepoints and radiological imaging assessment

rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA HNSCC UNM_OP.csv", stringsAsFactors = FALSE)

names(df) <- trimws(names(df))

# ---- Helper: compute median interval per patient ----
median_interval_per_patient <- function(data, filter_col, filter_val) {
  data %>%
    filter(!!sym(filter_col) == filter_val) %>%
    arrange(PatientName, date.diff) %>%
    group_by(PatientName) %>%
    mutate(interval = date.diff - lag(date.diff)) %>%
    summarise(median_interval = median(interval, na.rm = TRUE), .groups = "drop") %>%
    mutate(event_group = filter_val)
}

# ---- Compute per-patient medians ----
ctDNA_patient_medians   <- median_interval_per_patient(df, "Event", "ctDNA")
imaging_patient_medians <- median_interval_per_patient(df, "Event_type", "Imaging")

# ---- Function to summarize patient-level medians to cohort-level ----
cohort_summary <- function(patient_data, event_label) {
  # Filter out patients with NA median intervals
  valid_data <- patient_data %>% filter(!is.na(median_interval))
  
  data.frame(
    Event_Type = event_label,
    Cohort_Median_Frequency = median(valid_data$median_interval, na.rm = TRUE),
    Min_Patient_Median = min(valid_data$median_interval, na.rm = TRUE),
    Max_Patient_Median = max(valid_data$median_interval, na.rm = TRUE),
    Range_Patient_Median = max(valid_data$median_interval, na.rm = TRUE) - 
      min(valid_data$median_interval, na.rm = TRUE)
  )
}

# ---- Combine all results ----
results <- bind_rows(
  cohort_summary(ctDNA_patient_medians, "ctDNA"),
  cohort_summary(imaging_patient_medians, "Imaging")
)

# ---- Print cohort-level summary ----
cat("===== Median Frequency (Days) per Cohort =====\n")
===== Median Frequency (Days) per Cohort =====
print(results, row.names = FALSE)
 Event_Type Cohort_Median_Frequency Min_Patient_Median Max_Patient_Median Range_Patient_Median
      ctDNA                    88.0                 18                303                  285
    Imaging                   246.5                 22               1068                 1046
# ---- OPTIONAL: Save patient-level medians ----
patient_level_medians <- bind_rows(ctDNA_patient_medians, imaging_patient_medians)
#write.csv(patient_level_medians, "patient_median_intervals.csv", row.names = FALSE)

#Plot for individual lead-time calculations for each pt

rm(list=ls())
csv_path <- "~/Downloads/CLIA HNSCC Peddada_Lead Time pts.csv"
df <- read.csv(csv_path, check.names = FALSE)
id_candidates <- c("Patient","ID","Pt","Subject","Sample")
id_col <- id_candidates[id_candidates %in% names(df)][1]
if (is.na(id_col)) {
  df <- df %>% mutate(Patient = row_number())
  id_col <- "Patient"
}

num_cols <- intersect(c("MR","CR","LT"), names(df))
df[num_cols] <- lapply(df[num_cols], function(x) suppressWarnings(as.numeric(x)))

# If LT missing, compute in days
if (!("LT" %in% names(df))) {
  df <- df %>% mutate(LT = CR - MR)
}

# ---- convert to months ----
# Approximate: 30.44 days per month (average)
days_to_months <- function(x) x / 30.437

df <- df %>%
  mutate(MR_mo = days_to_months(MR),
         CR_mo = days_to_months(CR),
         LT_mo = days_to_months(LT))

# ---- ordering for y-axis ----
df <- df %>%
  mutate(Earliest = pmin(MR_mo, CR_mo, na.rm = TRUE)) %>%
  arrange(Earliest) %>%
  mutate(Patient_f = factor(.data[[id_col]], levels = .data[[id_col]]))

# ---- long format for points ----
points_long <- df %>%
  select(Patient_f, MR_mo, CR_mo) %>%
  pivot_longer(c(MR_mo, CR_mo), names_to = "Type", values_to = "Months") %>%
  mutate(Type = dplyr::recode(Type,
                              "MR_mo" = "Molecular recurrence",
                              "CR_mo" = "Clinical recurrence"))

# ---- segments between MR and CR ----
segments_df <- df %>%
  transmute(Patient_f,
            x0 = MR_mo, x1 = CR_mo,
            lt_flag = if_else(LT > 120, "gt120", "le120"))

# ---- annotation ----
med_lt  <- median(df$LT_mo, na.rm = TRUE)
min_lt  <- min(df$LT_mo, na.rm = TRUE)
max_lt  <- max(df$LT_mo, na.rm = TRUE)

annot_label <- sprintf("Median lead-time:\n%.1f months (%.1f to %.1f)",
                       med_lt, min_lt, max_lt)

# ---- plot ----
pal <- c("Molecular recurrence" = "#10B4C1",
         "Clinical recurrence"  = "#C96A72")

x_max <- max(c(df$MR_mo, df$CR_mo), na.rm = TRUE)
y_mid <- levels(df$Patient_f)[ceiling(nlevels(df$Patient_f) * 0.55)]

p <- ggplot() +
  geom_segment(data = segments_df,
               aes(x = x0, xend = x1, y = Patient_f, yend = Patient_f,
                   linetype = lt_flag),
               linewidth = 0.6, color = "black") +
  scale_linetype_manual(values = c(le120 = "solid", gt120 = "dashed"),
                        guide = "none") +
  geom_point(data = points_long,
             aes(x = Months, y = Patient_f, color = Type),
             size = 2.8) +
  scale_color_manual(values = pal, name = NULL) +
  labs(x = "Months from Surgery or Definitive Treatment", y = NULL) +
  annotate("text",
           x = x_max * 0.73,
           y = y_mid,
           label = annot_label,
           hjust = 0, vjust = 0.5, size = 3.8) +
  scale_x_continuous(breaks = seq(0, ceiling(x_max/3)*3, by = 3)) +
  coord_cartesian(xlim = c(0, x_max * 1.05)) +
  theme_bw(base_size = 12) +
  theme(
    panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
    panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
    axis.text.y = element_text(size = 9)
  )

print(p)


wc_df <- df %>% dplyr::select(MR, CR) %>% tidyr::drop_na()
paired_diff <- wc_df$CR - wc_df$MR

wilx <- wilcox.test(
  x = wc_df$CR, y = wc_df$MR,
  paired = TRUE,
  alternative = "two.sided",
  conf.int = TRUE,      # gives CI for the Hodges–Lehmann estimate of the median difference
  exact = FALSE         # safer for ties/large N
)

W_stat   <- unname(wilx$statistic)          # Wilcoxon V
p_value  <- wilx$p.value
HL_est   <- unname(wilx$estimate)           # median of (CR - MR)
HL_ci    <- wilx$conf.int                   # CI for median difference

# Simple p-value formatter for annotation/publication
fmt_p <- function(p) {
  if (is.na(p)) return("NA")
  if (p < 0.001) "< 0.001" else sprintf("= %.3f", round(p, 3))
}
p_text <- paste0("P ", fmt_p(p_value))  # e.g., "P = 0.003" or "P < 0.001"

# Optional: print a compact summary
cat("\nPaired Wilcoxon signed-rank test (CR vs MR)\n",
    "-------------------------------------------\n",
    sprintf("N pairs: %d", nrow(wc_df)), "\n",
    sprintf("W (V): %g", W_stat), "\n",
    sprintf("P-value: %s", ifelse(p_value < 0.001, "< 0.001", sprintf("%.6f", p_value))), "\n",
    sprintf("Hodges–Lehmann median difference (CR - MR): %.1f days", HL_est), "\n",
    sprintf("95%% CI: [%.1f, %.1f] days", HL_ci[1], HL_ci[2]), "\n", sep = "")

Paired Wilcoxon signed-rank test (CR vs MR)
-------------------------------------------
N pairs: 22
W (V): 253
P-value: < 0.001
Hodges–Lehmann median difference (CR - MR): 145.5 days
95% CI: [86.5, 248.5] days

#ctDNA velocity and lead time liner regression

csv_path <- "~/Downloads/CLIA HNSCC Peddada_ctDNA velocity.csv"  # <- adjust if needed
df_raw <- read.csv(csv_path, check.names = FALSE)
df <- df_raw %>%
  rename(MTM_mL = `MTM/mL`) %>%
  mutate(
    daysCR.months = as.numeric(daysCR.months),
    MTM_mL = as.numeric(MTM_mL)
  ) %>%
  filter(!is.na(PatientName), !is.na(daysCR.months), !is.na(MTM_mL))

preCR <- df %>%
  filter(daysCR.months <= 0, is.finite(MTM_mL), MTM_mL > 0)

eligible <- preCR %>%
  group_by(PatientName) %>%
  filter(n() >= 2, n_distinct(daysCR.months) >= 2) %>%
  ungroup()
if (nrow(eligible) == 0) {
  warning("No patients have ≥2 valid pre-recurrence points with distinct times; regression lines will be omitted.")
}

fits <- eligible %>%
  group_by(PatientName) %>%
  summarise(x_min = min(daysCR.months, na.rm = TRUE), .groups = "drop") %>%
  mutate(grid = map(x_min, ~seq(.x, 0, length.out = 50))) %>%
  select(PatientName, grid)

predict_patient <- function(dat, newx) {
  if (length(newx) == 0) {
    return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
  }
  dat2 <- dat %>%
    filter(is.finite(MTM_mL), MTM_mL > 0) %>%
    mutate(log_ctdna = log10(MTM_mL))
  if (nrow(dat2) < 2 || n_distinct(dat2$daysCR.months) < 2 || any(!is.finite(dat2$log_ctdna))) {
    return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
  }
  m <- lm(log_ctdna ~ daysCR.months, data = dat2)
  tibble(
    daysCR.months = newx,
    MTM_mL = 10 ^ predict(m, newdata = tibble(daysCR.months = newx))
  )
}

pred_lines <- eligible %>%
  group_by(PatientName) %>%
  tidyr::nest() %>%                       # list-column "data" per patient
  left_join(fits, by = "PatientName") %>% # list-column "grid" per patient
  mutate(pred = map2(data, grid, ~predict_patient(.x, .y))) %>%
  select(PatientName, pred) %>%
  tidyr::unnest(pred)

pooled_line <- {
  if (nrow(preCR) >= 2 && n_distinct(preCR$daysCR.months) >= 2) {
    pooled_x <- seq(min(preCR$daysCR.months, na.rm = TRUE), 0, length.out = 100)
    pooled_fit <- lm(log10(MTM_mL) ~ daysCR.months, data = preCR)
    tibble(
      daysCR.months = pooled_x,
      MTM_mL = 10 ^ predict(pooled_fit, newdata = tibble(daysCR.months = pooled_x))
    )
  } else {
    tibble(daysCR.months = numeric(0), MTM_mL = numeric(0))
  }
}

x_min <- floor(min(df$daysCR.months, na.rm = TRUE) / 3) * 3
x_max <- ceiling(max(df$daysCR.months, na.rm = TRUE) / 3) * 3
y_min_pos <- max(min(df$MTM_mL[df$MTM_mL > 0], na.rm = TRUE) / 2, 0.01)
y_max_pos <- 10 ^ ceiling(log10(max(df$MTM_mL, na.rm = TRUE)))
log_breaks <- 10 ^ seq(floor(log10(y_min_pos)), ceiling(log10(y_max_pos)))

p <- ggplot() +
  # per-patient fitted lines (if any)
  geom_line(data = pred_lines,
            aes(x = daysCR.months, y = MTM_mL, color = PatientName),
            linewidth = 1) +
  # pooled dashed trend (if any)
  geom_line(data = pooled_line,
            aes(x = daysCR.months, y = MTM_mL),
            linewidth = 0.8, linetype = "dashed", color = "grey35") +
  # raw points (all timepoints, before and after)
  geom_point(data = df,
             aes(x = daysCR.months, y = MTM_mL, color = PatientName),
             size = 1.8, alpha = 0.85) +
  # vertical line at imaging-positive date
  geom_vline(xintercept = 0, linetype = "dashed", linewidth = 0.8, color = "black") +
  scale_y_log10(breaks = log_breaks,
                labels = label_number(accuracy = 1)) +
  scale_x_continuous(breaks = seq(x_min, x_max, by = 3)) +  # every 3 months
  labs(
    x = "Months before/after clinical recurrence (0 = imaging positive)",
    y = "ctDNA level (MTM/mL)",
    color = "Patient"
  ) +
  theme_bw(base_size = 12) +
  theme(
    panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
    panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
    legend.position = "none"  # change to "right" if you want the legend
  )

print(p)

#Patient Specific Plot for UNM-069

rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("CLIA HNSCC_Pt Specific Plot data.csv")

library(ggplot2)
library(dplyr)
library(scales)

# 1. FILTER FOR PATIENT
target_patient <- "UNM-069"
plot_data <- circ_data %>% filter(PatientName == target_patient)

# 2. Process Data
# We fix the pseudo-zero for log scale but allow high values (like 18.08) to remain
data_Signatera <- plot_data %>% 
  filter(Event_type %in% c("ctDNA_pos", "ctDNA_neg")) %>%
  mutate(MTM_Log = ifelse(MTM.mL == 0, 0.001, MTM.mL))

data_Imaging_NED <- plot_data %>% filter(Event_type == "Imaging" & Event == "NED")
data_Imaging_PD <- plot_data %>% filter(Event_type == "Imaging" & Event == "PD")
data_Biopsy <- plot_data %>% filter(Event_type == "Biopsy" | Event == "Biopsy")

data_Treatment <- plot_data %>% 
  filter(!is.na(Tx_type) & Tx_type != "NA" & Tx_type != "" & !is.na(Tx_start.months)) %>%
  distinct(Tx_type, Tx_start.months, Tx_end.months)

# 3. Create the Plot
ggplot() +
  # Treatment Rectangles - Expanded ymax to match new scale
  geom_rect(data = data_Treatment, 
            aes(xmin = Tx_start.months, xmax = Tx_end.months, ymin = 0.004, ymax = 100), 
            fill = "purple", alpha = 0.15) +
  geom_text(data = data_Treatment, 
            aes(x = (Tx_start.months + Tx_end.months)/2, y = 50, label = Tx_type), 
            angle = 90, color = "purple", size = 3, fontface = "bold") +
  
  # ctDNA Line - Now it will connect because the limits are higher
  geom_line(data = data_Signatera, aes(x = date.diff.months, y = MTM_Log), 
            color = "black", linewidth = 0.7) +
  
  # ctDNA Points
  geom_point(data = data_Signatera, 
             aes(x = date.diff.months, y = MTM_Log, fill = Event_type), 
             shape = 21, size = 3.5, color = "black") +
  
  # Clinical Events
  geom_point(data = data_Imaging_NED, aes(x = date.diff.months, y = 0.005, color = "NED in Scan"), 
             shape = 25, size = 4, fill = "green") +
  geom_point(data = data_Imaging_PD, aes(x = date.diff.months, y = 0.005, color = "PD (Progression)"), 
             shape = 25, size = 4, fill = "red") +
  geom_point(data = data_Biopsy, aes(x = date.diff.months, y = 0.005, color = "Biopsy"), 
             shape = 23, size = 4, fill = "orange") +
  
  # X-Axis: 3-month intervals
  scale_x_continuous(breaks = seq(0, max(plot_data$date.diff.months, na.rm=T) + 6, by = 3)) +
  
  # Y-Axis: EXPANDED Log10 scale to include 18.08
  scale_y_log10(breaks = c(0.001, 0.01, 0.1, 1, 10, 100),
                labels = c("0", "0.01", "0.1", "1", "10", "100"),
                limits = c(0.001, 100)) +
  
  scale_fill_manual(values = c("ctDNA_pos" = "black", "ctDNA_neg" = "white"), 
                    labels = c("Negative", "Positive"), name = "ctDNA Status") +
  
  scale_color_manual(values = c("NED in Scan" = "darkgreen", 
                                "PD (Progression)" = "darkred", 
                                "Biopsy" = "darkorange"), 
                     name = "Clinical Events") +
  
  guides(color = guide_legend(override.aes = list(shape = c(23, 25, 25), 
                                                  fill = c("orange", "green", "red")))) +
  
  labs(x = "Months from Diagnosis/Surgery", 
       y = "MTM/mL", 
       title = paste("Clinical Timeline:", target_patient)) +
  theme_minimal() +
  theme(panel.grid.minor = element_blank())

LS0tCnRpdGxlOiAiSE5TQ0MgVU5NIEFsdGhvZmYgZXQgYWxfQ2xpbmljYWwgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmxpYnJhcnkoc3dpbXBsb3QpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShndGFibGUpCmxpYnJhcnkocmVhZHIpIApsaWJyYXJ5KG1vc2FpYykKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGRwbHlyKSAKbGlicmFyeShzdXJ2aXZhbCkgCmxpYnJhcnkoc3Vydm1pbmVyKSAKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShjb3hwaGYpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGd0c3VtbWFyeSkKbGlicmFyeShmbGV4dGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkocGFyYW1ldGVycykKbGlicmFyeShjYXIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkocm1zKQoKI2N0RE5BIERldGVjdGlvbiBSYXRlcyBieSBXaW5kb3cgYW5kIFN0YWdlcwpgYGB7cn0KI2N0RE5BIGF0IEJhc2VsaW5lCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UhPSIiLF0KY2lyY19kYXRhJGN0RE5BLkJhc2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5CYXNlLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjaXJjX2RhdGEgPC0gc3Vic2V0KGNpcmNfZGF0YSwgY3RETkEuQmFzZSAlaW4lIGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHM9YygiSS9JSSIsIklJSS9JVkEvSVZCIiwiSVZDIikpCnBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSA8LSBhZ2dyZWdhdGUoY2lyY19kYXRhJGN0RE5BLkJhc2UgPT0gIlBPU0lUSVZFIiwgYnk9bGlzdChjaXJjX2RhdGEkU3RhZ2UpLCBGVU49c3VtKQp0b3RhbF9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5CYXNlLCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1sZW5ndGgpCmNvbWJpbmVkX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9IHRvdGFsX2NvdW50c19ieV9zdGFnZSRHcm91cC4xLAogIFRvdGFsX0NvdW50ID0gdG90YWxfY291bnRzX2J5X3N0YWdlJHgsCiAgUG9zaXRpdmVfQ291bnQgPSBwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UkeCwKICBSYXRlID0gKHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4IC8gdG90YWxfY291bnRzX2J5X3N0YWdlJHgpICogMTAwICAjIENvbnZlcnQgdG8gcGVyY2VudGFnZQopCmNvbWJpbmVkX2RhdGEkUmF0ZSA8LSBzcHJpbnRmKCIlLjJmJSUiLCBjb21iaW5lZF9kYXRhJFJhdGUpCm92ZXJhbGxfdG90YWxfY291bnQgPC0gbnJvdyhjaXJjX2RhdGEpCm92ZXJhbGxfcG9zaXRpdmVfY291bnQgPC0gbnJvdyhjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UgPT0gIlBPU0lUSVZFIixdKQpvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSA8LSAob3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCAvIG92ZXJhbGxfdG90YWxfY291bnQpICogMTAwICAjIENvbnZlcnQgdG8gcGVyY2VudGFnZQpvdmVyYWxsX3JvdyA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gIk92ZXJhbGwiLAogIFRvdGFsX0NvdW50ID0gb3ZlcmFsbF90b3RhbF9jb3VudCwKICBQb3NpdGl2ZV9Db3VudCA9IG92ZXJhbGxfcG9zaXRpdmVfY291bnQsCiAgUmF0ZSA9IHNwcmludGYoIiUuMmYlJSIsIG92ZXJhbGxfcG9zaXRpdml0eV9yYXRlKQopCmNvbWJpbmVkX2RhdGEgPC0gcmJpbmQoY29tYmluZWRfZGF0YSwgb3ZlcmFsbF9yb3cpCnByaW50KGNvbWJpbmVkX2RhdGEpCgojY3RETkEgYXQgTVJECnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCE9IiIsXQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjaXJjX2RhdGEkU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRTdGFnZSwgbGV2ZWxzPWMoIkkvSUkiLCJJSUkvSVZBL0lWQiIsIklWQyIpKQpwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5NUkQgPT0gIlBPU0lUSVZFIiwgYnk9bGlzdChjaXJjX2RhdGEkU3RhZ2UpLCBGVU49c3VtKQp0b3RhbF9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5NUkQsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsXSkKb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUgPC0gKG92ZXJhbGxfcG9zaXRpdmVfY291bnQgLyBvdmVyYWxsX3RvdGFsX2NvdW50KSAqIDEwMCAgIyBDb252ZXJ0IHRvIHBlcmNlbnRhZ2UKb3ZlcmFsbF9yb3cgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9ICJPdmVyYWxsIiwKICBUb3RhbF9Db3VudCA9IG92ZXJhbGxfdG90YWxfY291bnQsCiAgUG9zaXRpdmVfQ291bnQgPSBvdmVyYWxsX3Bvc2l0aXZlX2NvdW50LAogIFJhdGUgPSBzcHJpbnRmKCIlLjJmJSUiLCBvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSkKKQpjb21iaW5lZF9kYXRhIDwtIHJiaW5kKGNvbWJpbmVkX2RhdGEsIG92ZXJhbGxfcm93KQpwcmludChjb21iaW5lZF9kYXRhKQoKI2N0RE5BIGF0IFN1cnZlaWxsYW5jZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhJFN0YWdlIDwtIGZhY3RvcihjaXJjX2RhdGEkU3RhZ2UsIGxldmVscz1jKCJJL0lJIiwiSUlJL0lWQS9JVkIiLCJJVkMiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlID09ICJQT1NJVElWRSIsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPXN1bSkKdG90YWxfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1sZW5ndGgpCmNvbWJpbmVkX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9IHRvdGFsX2NvdW50c19ieV9zdGFnZSRHcm91cC4xLAogIFRvdGFsX0NvdW50ID0gdG90YWxfY291bnRzX2J5X3N0YWdlJHgsCiAgUG9zaXRpdmVfQ291bnQgPSBwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UkeCwKICBSYXRlID0gKHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4IC8gdG90YWxfY291bnRzX2J5X3N0YWdlJHgpICogMTAwICAjIENvbnZlcnQgdG8gcGVyY2VudGFnZQopCmNvbWJpbmVkX2RhdGEkUmF0ZSA8LSBzcHJpbnRmKCIlLjJmJSUiLCBjb21iaW5lZF9kYXRhJFJhdGUpCm92ZXJhbGxfdG90YWxfY291bnQgPC0gbnJvdyhjaXJjX2RhdGEpCm92ZXJhbGxfcG9zaXRpdmVfY291bnQgPC0gbnJvdyhjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA9PSAiUE9TSVRJVkUiLF0pCm92ZXJhbGxfcG9zaXRpdml0eV9yYXRlIDwtIChvdmVyYWxsX3Bvc2l0aXZlX2NvdW50IC8gb3ZlcmFsbF90b3RhbF9jb3VudCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCm92ZXJhbGxfcm93IDwtIGRhdGEuZnJhbWUoCiAgU3RhZ2UgPSAiT3ZlcmFsbCIsCiAgVG90YWxfQ291bnQgPSBvdmVyYWxsX3RvdGFsX2NvdW50LAogIFBvc2l0aXZlX0NvdW50ID0gb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCwKICBSYXRlID0gc3ByaW50ZigiJS4yZiUlIiwgb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUpCikKY29tYmluZWRfZGF0YSA8LSByYmluZChjb21iaW5lZF9kYXRhLCBvdmVyYWxsX3JvdykKcHJpbnQoY29tYmluZWRfZGF0YSkKYGBgCgoKCiNEZW1vZ3JhcGhpY3MgVGFibGUKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQoKY2lyY19kYXRhX3N1YnNldCA8LSBjaXJjX2RhdGEgJT4lCiAgc2VsZWN0KAogICAgU2V4LAogICAgQWdlLAogICAgVG9iYWNjby5IaXN0b3J5LAogICAgUHJpbS5Mb2NhdGlvbiwKICAgIGNULAogICAgY04sCiAgICBjTSwKICAgIEhpc3RvbG9neSwKICAgIFN0YWdlLAogICAgcDE2LnN0YXR1cywKICAgIFRyZWF0bWVudC5Hcm91cCwKICAgIFBGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgT1MubW9udGhzKSAlPiUKICBtdXRhdGUoCiAgICBTZXggPSBmYWN0b3IoU2V4KSwKICAgIEFnZSA9IGFzLm51bWVyaWMoQWdlKSwKICAgIFRvYmFjY28uSGlzdG9yeSA9IGZhY3RvcihUb2JhY2NvLkhpc3RvcnkpLAogICAgUHJpbS5Mb2NhdGlvbiA9IGZhY3RvcihQcmltLkxvY2F0aW9uKSwKICAgIGNUID0gZmFjdG9yKGNUKSwKICAgIGNOID0gZmFjdG9yKGNOKSwKICAgIGNNID0gZmFjdG9yKGNNKSwKICAgIEhpc3RvbG9neSA9IGZhY3RvcihIaXN0b2xvZ3kpLAogICAgU3RhZ2UgPSBmYWN0b3IoU3RhZ2UpLAogICAgcDE2LnN0YXR1cyA9IGZhY3RvcihwMTYuc3RhdHVzKSwKICAgIFRyZWF0bWVudC5Hcm91cCA9IGZhY3RvcihUcmVhdG1lbnQuR3JvdXApLAogICAgUEZTLkV2ZW50ID0gZmFjdG9yKFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKSwKICAgIE9TLkV2ZW50ID0gZmFjdG9yKE9TLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIkFsaXZlIiwgIkRlY2Vhc2VkIikpLAogICAgT1MubW9udGhzID0gYXMubnVtZXJpYyhPUy5tb250aHMpKSAKdGFibGUxIDwtIGNpcmNfZGF0YV9zdWJzZXQgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWRpYW59ICh7bWlufSAtIHttYXh9KSIsCiAgICAgIGFsbF9jYXRlZ29yaWNhbCgpIH4gIntufSAoe3B9JSkiKSkgJT4lCiAgYm9sZF9sYWJlbHMoKQp0YWJsZTEKZml0MSA8LSBhc19mbGV4X3RhYmxlKAogIHRhYmxlMSwKICBpbmNsdWRlID0gZXZlcnl0aGluZygpLAogIHJldHVybl9jYWxscyA9IEZBTFNFCikKZml0MQpzYXZlX2FzX2RvY3goZml0MSwgcGF0aD0gIn4vRG93bmxvYWRzLzEuIENMSUEgSE5TQ0MgVU5NIERlbW9ncmFwaGljcyBUYWJsZS5kb2N4IikKYGBgCgoKCiNEZW1vZ3JhcGhpY3MgVGFibGUgYnkgY3RETkEgYXQgYmFzZWxpbmUKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCgpjaXJjX2RhdGFfc3Vic2V0MSA8LSBjaXJjX2RhdGEgJT4lCiAgc2VsZWN0KAogICAgU2V4LAogICAgQWdlLAogICAgVG9iYWNjby5IaXN0b3J5LAogICAgUHJpbS5Mb2NhdGlvbiwKICAgIGNULAogICAgY04sCiAgICBjTSwKICAgIEhpc3RvbG9neSwKICAgIFN0YWdlLAogICAgcDE2LnN0YXR1cywKICAgIFRyZWF0bWVudC5Hcm91cCwKICAgIFBGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgT1MubW9udGhzKSAlPiUKICBtdXRhdGUoCiAgICBTZXggPSBmYWN0b3IoU2V4KSwKICAgIEFnZSA9IGFzLm51bWVyaWMoQWdlKSwKICAgIFRvYmFjY28uSGlzdG9yeSA9IGZhY3RvcihUb2JhY2NvLkhpc3RvcnkpLAogICAgUHJpbS5Mb2NhdGlvbiA9IGZhY3RvcihQcmltLkxvY2F0aW9uKSwKICAgIGNUID0gZmFjdG9yKGNUKSwKICAgIGNOID0gZmFjdG9yKGNOKSwKICAgIGNNID0gZmFjdG9yKGNNKSwKICAgIEhpc3RvbG9neSA9IGZhY3RvcihIaXN0b2xvZ3kpLAogICAgU3RhZ2UgPSBmYWN0b3IoU3RhZ2UpLAogICAgcDE2LnN0YXR1cyA9IGZhY3RvcihwMTYuc3RhdHVzKSwKICAgIFRyZWF0bWVudC5Hcm91cCA9IGZhY3RvcihUcmVhdG1lbnQuR3JvdXApLAogICAgUEZTLkV2ZW50ID0gZmFjdG9yKFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKSwKICAgIE9TLkV2ZW50ID0gZmFjdG9yKE9TLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIkFsaXZlIiwgIkRlY2Vhc2VkIikpLAogICAgT1MubW9udGhzID0gYXMubnVtZXJpYyhPUy5tb250aHMpKSAKCmNpcmNfZGF0YTEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KCmNpcmNfZGF0YV9zdWJzZXQyIDwtIGNpcmNfZGF0YTEgJT4lCiAgc2VsZWN0KAogICAgU2V4LAogICAgQWdlLAogICAgVG9iYWNjby5IaXN0b3J5LAogICAgUHJpbS5Mb2NhdGlvbiwKICAgIGNULAogICAgY04sCiAgICBjTSwKICAgIEhpc3RvbG9neSwKICAgIFN0YWdlLAogICAgcDE2LnN0YXR1cywKICAgIFRyZWF0bWVudC5Hcm91cCwKICAgIFBGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgT1MubW9udGhzLAogICAgY3RETkEuQmFzZSkgJT4lCiAgbXV0YXRlKAogICAgU2V4ID0gZmFjdG9yKFNleCksCiAgICBBZ2UgPSBhcy5udW1lcmljKEFnZSksCiAgICBUb2JhY2NvLkhpc3RvcnkgPSBmYWN0b3IoVG9iYWNjby5IaXN0b3J5KSwKICAgIFByaW0uTG9jYXRpb24gPSBmYWN0b3IoUHJpbS5Mb2NhdGlvbiksCiAgICBjVCA9IGZhY3RvcihjVCksCiAgICBjTiA9IGZhY3RvcihjTiksCiAgICBjTSA9IGZhY3RvcihjTSksCiAgICBIaXN0b2xvZ3kgPSBmYWN0b3IoSGlzdG9sb2d5KSwKICAgIFN0YWdlID0gZmFjdG9yKFN0YWdlKSwKICAgIHAxNi5zdGF0dXMgPSBmYWN0b3IocDE2LnN0YXR1cyksCiAgICBUcmVhdG1lbnQuR3JvdXAgPSBmYWN0b3IoVHJlYXRtZW50Lkdyb3VwKSwKICAgIFBGUy5FdmVudCA9IGZhY3RvcihQRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUHJvZ3Jlc3Npb24iLCAiUHJvZ3Jlc3Npb24iKSksCiAgICBPUy5FdmVudCA9IGZhY3RvcihPUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJBbGl2ZSIsICJEZWNlYXNlZCIpKSwKICAgIE9TLm1vbnRocyA9IGFzLm51bWVyaWMoT1MubW9udGhzKSwKICAgIGN0RE5BLkJhc2UgPSBmYWN0b3IoY3RETkEuQmFzZSwgbGV2ZWxzID0gYygiTkVHQVRJVkUiLCAiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkpCk92ZXJhbGwgPC0gY2lyY19kYXRhX3N1YnNldDEgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWRpYW59ICh7bWlufSAtIHttYXh9KSIsCiAgICAgIGFsbF9jYXRlZ29yaWNhbCgpIH4gIntufSAoe3B9JSkiKSkgJT4lCiAgYm9sZF9sYWJlbHMoKQpPdmVyYWxsCgpCeWN0RE5BX01SRCA8LSBjaXJjX2RhdGFfc3Vic2V0MiAlPiUKICB0Ymxfc3VtbWFyeSgKICAgIGJ5ID0gY3RETkEuQmFzZSwgIyBhZGQgdGhpcyBsaW5lIHRvIHN1Ymdyb3VwIGJ5IGN0RE5BLkJhc2UKICAgIHN0YXRpc3RpYyA9IGxpc3QoCiAgICAgIGFsbF9jb250aW51b3VzKCkgfiAie21lZGlhbn0gKHttaW59IC0ge21heH0pIiwKICAgICAgYWxsX2NhdGVnb3JpY2FsKCkgfiAie259ICh7cH0lKSIpKSAlPiUKICBhZGRfcCgpICU+JQogIGJvbGRfbGFiZWxzKCkKQnljdEROQV9NUkQKCm1lcmdlZF90YWJsZSA8LSB0YmxfbWVyZ2UodGJscz1saXN0KE92ZXJhbGwsIEJ5Y3RETkFfTVJEKSkKbWVyZ2VkX3RhYmxlCgpmaXQxIDwtIGFzX2ZsZXhfdGFibGUoCiAgbWVyZ2VkX3RhYmxlLAogIGluY2x1ZGUgPSBldmVyeXRoaW5nKCksCiAgcmV0dXJuX2NhbGxzID0gRkFMU0UKKQpmaXQxCnNhdmVfYXNfZG9jeChmaXQxLCBwYXRoID0gIn4vRG93bmxvYWRzLzFiLiBDTElBIEhOU0NDIFVOTSBEZW1vZ3JhcGhpY3MgVGFibGUgYnkgY3RETkEuZG9jeCIpCmBgYAoKI092ZXJ2aWV3IHBsb3QgYnkgU3RhZ2UKYGBge3J9CnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjbGluc3RhZ2UgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgVU5NX09QLmNzdiIpCmNsaW5zdGFnZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNsaW5zdGFnZSkKCiMgQ3JlYXRpbmcgdGhlIGJhc2ljIHN3aW1tZXIgcGxvdApvcGxvdCA8LSBzd2ltbWVyX3Bsb3QoZGY9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgICAgaWQ9J1BhdGllbnROYW1lJywKICAgICAgICAgICAgICAgICAgICAgIGVuZD0nZnUuZGlmZi5tb250aHMnLAogICAgICAgICAgICAgICAgICAgICAgZmlsbD0nZ3JheScsCiAgICAgICAgICAgICAgICAgICAgICB3aWR0aD0uMDEsCiAgICAgICAgICAgICAgICAgICAgICBiYXNlX3NpemUgPSAxNCwKICAgICAgICAgICAgICAgICAgICAgIHN0cmF0aWZ5PSBjKCdTdGFnZScpKQoKIyBBZGRpbmcgdGhlbWVzIGFuZCBzY2FsZXMKb3Bsb3QgPC0gb3Bsb3QgKyB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpCm9wbG90IDwtIG9wbG90ICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA3MiwgYnkgPSAzKSkKb3Bsb3QgPC0gb3Bsb3QgKyBsYWJzKHggPSJQYXRpZW50cyIsIHk9Ik1vbnRocyBmcm9tIERpYWdub3NpcyIpCgojIEFkZGluZyBzd2ltbWVyIHBvaW50cwpvcGxvdF9ldjEgPC0gb3Bsb3QgKyBzd2ltbWVyX3BvaW50cyhkZl9wb2ludHM9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lPSdkYXRlLmRpZmYubW9udGhzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9zaGFwZSA9J0V2ZW50X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2NvbCA9ICdFdmVudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LGZpbGw9J2JsYWNrJykKIyBPcHRpb25hbGx5IHVuY29tbWVudCBhbmQgdXNlIGNvbD0nZGFya2dyZWVuJyBpZiBuZWVkZWQKCiMgQWRkaW5nIHNoYXBlIG1hbnVhbCBzY2FsZQpvcGxvdF9ldjEuMSA8LSBvcGxvdF9ldjEgKyBnZ3Bsb3QyOjpzY2FsZV9zaGFwZV9tYW51YWwobmFtZT0iRXZlbnRfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9YygxLDE2LDYsMTgsMTgsNCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3M9YygnY3RETkFfbmVnJywnY3RETkFfcG9zJywgJ0ltYWdpbmcnLCdTdXJnZXJ5JywnQmlvcHN5JywgJ0RlYXRoJykpCgojIERpc3BsYXkgdGhlIHBsb3QKb3Bsb3RfZXYxLjEKb3Bsb3RfZXYyIDwtIG9wbG90X2V2MS4xICsgc3dpbW1lcl9saW5lcyhkZl9saW5lcz1jbGluc3RhZ2VfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQ9J1BhdGllbnROYW1lJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydD0nVHhfc3RhcnQubW9udGhzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQ9J1R4X2VuZC5tb250aHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfY29sPSdUeF90eXBlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTMuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2FscGhhID0gMS4wKQpvcGxvdF9ldjIgPC0gb3Bsb3RfZXYyICsgZ3VpZGVzKGxpbmV0eXBlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDUsIGNvbG9yID0gImJsYWNrIikpKQpvcGxvdF9ldjIKb3Bsb3RfZXYyLjIgPC0gb3Bsb3RfZXYyICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkV2ZW50Iix2YWx1ZXM9YyggImdyZXkiLCAib3JhbmdlIiwgImJsYWNrIiwgImJsYWNrIiwgImdyZWVuIiwgInJlZCIsICJwdXJwbGUiLCAiYmx1ZSIpKQpvcGxvdF9ldjIuMgpgYGAKCiNQRlMgaW4gQ29tcGxldGUgQ29ob3J0IChOPTk3KQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLmF2YWlsYWJsZSwgZGF0YSA9IGNpcmNfZGF0YSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuYXZhaWxhYmxlLCBkYXRhID0gY2lyY19kYXRhLGNvbmYuaW50PTAuOTUsY29uZi50eXBlPSJsb2ctbG9nIikgCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiksIHRpdGxlPSJQRlMgLSBDb21wbGV0ZSBDb2hvcnQgKG49OTcpIiwgeWxhYj0gIlByb2dyZXNzaW9uLUZyZWUgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBTdGFydCBvZiBkZWZpbml0aXZlIFRyZWF0bWVudCIsIGxlZ2VuZC5sYWJzPWMoIkNvbXBsZXRlIGNvaG9ydCIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDEyLCAyNCwgMzYpKQpgYGAKCiNPUyBpbiBDb21wbGV0ZSBDb2hvcnQgKE49OTcpCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJE9TLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkT1MuRXZlbnQpfmN0RE5BLmF2YWlsYWJsZSwgZGF0YSA9IGNpcmNfZGF0YSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkT1MubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRPUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLmF2YWlsYWJsZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIpLCB0aXRsZT0iT1MgLSBDb21wbGV0ZSBDb2hvcnQgKG49OTcpIiwgeWxhYj0gIk92ZXJhbGwgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBTdGFydCBvZiBkZWZpbml0aXZlIFRyZWF0bWVudCIsIGxlZ2VuZC5sYWJzPWMoIkNvbXBsZXRlIGNvaG9ydCIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDEyLCAyNCwgMzYpKQpgYGAKCiNBc3NvY2lhdGlvbiBvZiBCYXNlbGluZSBjdEROQSBNVE0gbGV2ZWxzIHdpdGggY2xpbmljb3BhdGhvbG9naWNhbCBmYWN0b3JzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnRhbGx5KH5jU3RhZ2UsIGRhdGE9Y2lyY19kYXRhLCBtYXJnaW5zID0gVFJVRSkKY2lyY19kYXRhJGNTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJGNTdGFnZSwgbGV2ZWxzID0gYygiSS9JSSIsIklJSS9JViIpLCBsYWJlbHMgPSBjKCJJL0lJIChuPTM0KSIsIklJSS9JViAobj0yOSkiKSkKYm94cGxvdChjdEROQS5CYXNlLk1UTX5jU3RhZ2UsIGRhdGE9Y2lyY19kYXRhLCBtYWluPSJjdEROQSBwcmUtdHJlYXRtZW50IE1UTSAtIFN0YWdlIiwgeGxhYj0iU3RhZ2UiLCB5bGFiPSJNVE0vbUwiLCBjb2w9IndoaXRlIixib3JkZXI9ImJsYWNrIiwgeWxpbSA9IGMoMCwgMjAwKSkKbWVkaWFuX2N0RE5BLlN0YWdlIDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjU3RhZ2UpICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3RETkFfQmFzZV9NVE0gPSBtZWRpYW4oY3RETkEuQmFzZS5NVE0sIG5hLnJtID0gVFJVRSkpCnByaW50KG1lZGlhbl9jdEROQS5TdGFnZSkKbTE8LXdpbGNveC50ZXN0KGN0RE5BLkJhc2UuTVRNIH4gY1N0YWdlLCBkYXRhPWNpcmNfZGF0YSwgbmEucm09VFJVRSwgZXhhY3Q9RkFMU0UsIGNvbmYuaW50PVRSVUUpCnByaW50KG0xKQoKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgp0YWxseSh+Y1Quc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbWFyZ2lucyA9IFRSVUUpCmNpcmNfZGF0YSRjVC5zdGF0dXMgPC0gZmFjdG9yKGNpcmNfZGF0YSRjVC5zdGF0dXMsIGxldmVscyA9IGMoIlQwLVQyIiwiVDMtVDQiKSwgbGFiZWxzID0gYygiVDAtVDIgKG49MjkpIiwiVDMtVDQgKG49MzQpIikpCmJveHBsb3QoY3RETkEuQmFzZS5NVE1+Y1Quc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbWFpbj0iY3RETkEgcHJlLXRyZWF0bWVudCBNVE0gLSBUIHN0YWdlIiwgeGxhYj0iVCBzdGFnZSIsIHlsYWI9Ik1UTS9tTCIsIGNvbD0id2hpdGUiLGJvcmRlcj0iYmxhY2siLCB5bGltID0gYygwLCAyMDApKQptZWRpYW5fY3RETkEuY1QgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGNULnN0YXR1cykgJT4lCiAgc3VtbWFyaXNlKG1lZGlhbl9jdEROQV9CYXNlX01UTSA9IG1lZGlhbihjdEROQS5CYXNlLk1UTSwgbmEucm0gPSBUUlVFKSkKcHJpbnQobWVkaWFuX2N0RE5BLmNUKQptMjwtd2lsY294LnRlc3QoY3RETkEuQmFzZS5NVE0gfiBjVC5zdGF0dXMsIGRhdGE9Y2lyY19kYXRhLCBuYS5ybT1UUlVFLCBleGFjdD1GQUxTRSwgY29uZi5pbnQ9VFJVRSkKcHJpbnQobTIpCgpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnRhbGx5KH5jVCwgZGF0YT1jaXJjX2RhdGEsIG1hcmdpbnMgPSBUUlVFKQpjaXJjX2RhdGEkY1QgPC0gZmFjdG9yKGNpcmNfZGF0YSRjVCwgbGV2ZWxzID0gYygiVDAiLCJUMSIsIlQyIiwiVDMiLCJUNCIpKQpib3hwbG90KGN0RE5BLkJhc2UuTVRNfmNULCBkYXRhPWNpcmNfZGF0YSwgbWFpbj0iY3RETkEgcHJlLXRyZWF0bWVudCBNVE0gLSBjVCBzdGF0dXMiLCB4bGFiPSJjVCBzdGF0dXMiLCB5bGFiPSJNVE0vbUwiLCBjb2w9IndoaXRlIixib3JkZXI9ImJsYWNrIiwgeWxpbSA9IGMoMCwgMjAwKSkKbWVkaWFuX2N0RE5BLmNUIDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjVCkgJT4lCiAgc3VtbWFyaXNlKG1lZGlhbl9jdEROQV9CYXNlX01UTSA9IG1lZGlhbihjdEROQS5CYXNlLk1UTSwgbmEucm0gPSBUUlVFKSkKcHJpbnQobWVkaWFuX2N0RE5BLmNUKQpwYWlyd2lzZV93aWxjb3ggPC0gcGFpcndpc2Uud2lsY294LnRlc3QoY2lyY19kYXRhJGN0RE5BLkJhc2UuTVRNLCBjaXJjX2RhdGEkY1QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ID0gRkFMU0UpCnByaW50KHBhaXJ3aXNlX3dpbGNveCkKCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGUgPT0gIlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSAhPSAiIixdCmNpcmNfZGF0YSRjVCA8LSBmYWN0b3IoY2lyY19kYXRhJGNULCBsZXZlbHMgPSBjKCJUMCIsICJUMSIsICJUMiIsICJUMyIsICJUNCIpKQpjaXJjX2RhdGEkY3RETkEuQmFzZS5NVE0gPC0gYXMubnVtZXJpYyhjaXJjX2RhdGEkY3RETkEuQmFzZS5NVE0pCmNUX2xldmVscyA8LSBsZXZlbHMoY2lyY19kYXRhJGNUKQpwX3ZhbHVlX21hdHJpeCA8LSBtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgoY1RfbGV2ZWxzKSwgbmNvbCA9IGxlbmd0aChjVF9sZXZlbHMpKQpyb3duYW1lcyhwX3ZhbHVlX21hdHJpeCkgPC0gY1RfbGV2ZWxzCmNvbG5hbWVzKHBfdmFsdWVfbWF0cml4KSA8LSBjVF9sZXZlbHMKCmZvciAoaSBpbiAxOmxlbmd0aChjVF9sZXZlbHMpKSB7CiAgZm9yIChqIGluIGk6bGVuZ3RoKGNUX2xldmVscykpIHsKICAgIGlmIChpICE9IGopIHsKICAgICAgIyBFeHRyYWN0IGRhdGEgZm9yIGJvdGggZ3JvdXBzCiAgICAgIGRhdGExIDwtIGNpcmNfZGF0YSAlPiUgZmlsdGVyKGNUID09IGNUX2xldmVsc1tpXSkgJT4lIHB1bGwoY3RETkEuQmFzZS5NVE0pCiAgICAgIGRhdGEyIDwtIGNpcmNfZGF0YSAlPiUgZmlsdGVyKGNUID09IGNUX2xldmVsc1tqXSkgJT4lIHB1bGwoY3RETkEuQmFzZS5NVE0pCiAgICAgIAogICAgICAjIFBlcmZvcm0gV2lsY294b24gdGVzdCBhbmQgc3RvcmUgcC12YWx1ZQogICAgICB0ZXN0X3Jlc3VsdCA8LSB3aWxjb3gudGVzdChkYXRhMSwgZGF0YTIsIGV4YWN0ID0gRkFMU0UpCiAgICAgIHBfdmFsdWVfbWF0cml4W2ksIGpdIDwtIHRlc3RfcmVzdWx0JHAudmFsdWUKICAgICAgcF92YWx1ZV9tYXRyaXhbaiwgaV0gPC0gdGVzdF9yZXN1bHQkcC52YWx1ZSAgIyBNYWtlIHN5bW1ldHJpYwogICAgfSBlbHNlIHsKICAgICAgcF92YWx1ZV9tYXRyaXhbaSwgal0gPC0gMSAgIyBTZWxmLWNvbXBhcmlzb24gPSAxCiAgICB9CiAgfQp9CgpwX3ZhbHVlX21hdHJpeFtpcy5uYShwX3ZhbHVlX21hdHJpeCldIDwtIDEuMDAKcF92YWx1ZV9kYXRhIDwtIG1lbHQocF92YWx1ZV9tYXRyaXgpCmNvbG5hbWVzKHBfdmFsdWVfZGF0YSkgPC0gYygiY1QxIiwgImNUMiIsICJwX3ZhbHVlIikKcF92YWx1ZV9kYXRhIDwtIHBfdmFsdWVfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4oCiAgICAgIHBfdmFsdWUgPCAwLjAwMSB+ICIqKioiLAogICAgICBwX3ZhbHVlIDwgMC4wMSB+ICIqKiIsCiAgICAgIHBfdmFsdWUgPCAwLjA1IH4gIioiLAogICAgICBUUlVFIH4gIiIKICAgICkKICApCgpnZ3Bsb3QocF92YWx1ZV9kYXRhLCBhZXMoeCA9IGNUMSwgeSA9IGNUMiwgZmlsbCA9IHBfdmFsdWUpKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuOCkgKyAgIyBUaGlja2VyIGdyaWQgbGluZXMgZm9yIHNlcGFyYXRpb24KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNiwgZm9udGZhY2UgPSAiYm9sZCIpICsgICMgU2lnbmlmaWNhbmNlIG1hcmtlcnMKICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIiwgbWlkcG9pbnQgPSAwLjA1KSArICAjIEdyYWRpZW50IGNvbG9ycwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAiUGFpcndpc2UgV2lsY294b24tVGVzdCBQLVZhbHVlcyAoY3RETkEuQmFzZS5NVE0gYnkgY1QpIiwKICAgICAgIHggPSAiY1QgU3RhdHVzIiwgeSA9ICJjVCBTdGF0dXMiLCBmaWxsID0gIlAtVmFsdWUiKQoKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgp0YWxseSh+Y04uc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbWFyZ2lucyA9IFRSVUUpCmNpcmNfZGF0YSRjTi5zdGF0dXMgPC0gZmFjdG9yKGNpcmNfZGF0YSRjTi5zdGF0dXMsIGxldmVscyA9IGMoIk4wIiwiTjEtTjMiKSwgbGFiZWxzID0gYygiTjAgKG49MTIpIiwiTjEtTjMgKG49NTEpIikpCmJveHBsb3QoY3RETkEuQmFzZS5NVE1+Y04uc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbWFpbj0iY3RETkEgcHJlLXRyZWF0bWVudCBNVE0gLSBjTiBzdGF0dXMiLCB4bGFiPSJjTiBzdGF0dXMiLCB5bGFiPSJNVE0vbUwiLCBjb2w9IndoaXRlIixib3JkZXI9ImJsYWNrIiwgeWxpbSA9IGMoMCwgMjAwKSkKbWVkaWFuX2N0RE5BLmNOIDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjTi5zdGF0dXMpICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3RETkFfQmFzZV9NVE0gPSBtZWRpYW4oY3RETkEuQmFzZS5NVE0sIG5hLnJtID0gVFJVRSkpCnByaW50KG1lZGlhbl9jdEROQS5jTikKbTM8LXdpbGNveC50ZXN0KGN0RE5BLkJhc2UuTVRNIH4gY04uc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbmEucm09VFJVRSwgZXhhY3Q9RkFMU0UsIGNvbmYuaW50PVRSVUUpCnByaW50KG0zKQoKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgp0YWxseSh+Y04sIGRhdGE9Y2lyY19kYXRhLCBtYXJnaW5zID0gVFJVRSkKY2lyY19kYXRhJGNOIDwtIGZhY3RvcihjaXJjX2RhdGEkY04sIGxldmVscyA9IGMoIk4wIiwiTjEiLCJOMiIsIk4zIikpCmJveHBsb3QoY3RETkEuQmFzZS5NVE1+Y04sIGRhdGE9Y2lyY19kYXRhLCBtYWluPSJjdEROQSBwcmUtdHJlYXRtZW50IE1UTSAtIE4gU3RhZ2UiLCB4bGFiPSJOIFN0YWdlIiwgeWxhYj0iTVRNL21MIiwgY29sPSJ3aGl0ZSIsYm9yZGVyPSJibGFjayIsIHlsaW0gPSBjKDAsIDUwMCkpCm1lZGlhbl9jdEROQS5jTiA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY04pICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3RETkFfQmFzZV9NVE0gPSBtZWRpYW4oY3RETkEuQmFzZS5NVE0sIG5hLnJtID0gVFJVRSkpCnByaW50KG1lZGlhbl9jdEROQS5jTikKcGFpcndpc2Vfd2lsY294IDwtIHBhaXJ3aXNlLndpbGNveC50ZXN0KGNpcmNfZGF0YSRjdEROQS5CYXNlLk1UTSwgY2lyY19kYXRhJGNOLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJub25lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGFjdCA9IEZBTFNFKQpwcmludChwYWlyd2lzZV93aWxjb3gpCgpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlID09ICJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UgIT0gIiIsXQpjaXJjX2RhdGEkY04gPC0gZmFjdG9yKGNpcmNfZGF0YSRjTiwgbGV2ZWxzID0gYygiTjAiLCJOMSIsIk4yIiwiTjMiKSkKY2lyY19kYXRhJGN0RE5BLkJhc2UuTVRNIDwtIGFzLm51bWVyaWMoY2lyY19kYXRhJGN0RE5BLkJhc2UuTVRNKQpjTl9sZXZlbHMgPC0gbGV2ZWxzKGNpcmNfZGF0YSRjTikKcF92YWx1ZV9tYXRyaXggPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKGNOX2xldmVscyksIG5jb2wgPSBsZW5ndGgoY05fbGV2ZWxzKSkKcm93bmFtZXMocF92YWx1ZV9tYXRyaXgpIDwtIGNOX2xldmVscwpjb2xuYW1lcyhwX3ZhbHVlX21hdHJpeCkgPC0gY05fbGV2ZWxzCgpmb3IgKGkgaW4gMTpsZW5ndGgoY05fbGV2ZWxzKSkgewogIGZvciAoaiBpbiBpOmxlbmd0aChjTl9sZXZlbHMpKSB7CiAgICBpZiAoaSAhPSBqKSB7CiAgICAgICMgRXh0cmFjdCBkYXRhIGZvciBib3RoIGdyb3VwcwogICAgICBkYXRhMSA8LSBjaXJjX2RhdGEgJT4lIGZpbHRlcihjTiA9PSBjTl9sZXZlbHNbaV0pICU+JSBwdWxsKGN0RE5BLkJhc2UuTVRNKQogICAgICBkYXRhMiA8LSBjaXJjX2RhdGEgJT4lIGZpbHRlcihjTiA9PSBjTl9sZXZlbHNbal0pICU+JSBwdWxsKGN0RE5BLkJhc2UuTVRNKQogICAgICAKICAgICAgIyBQZXJmb3JtIFdpbGNveG9uIHRlc3QgYW5kIHN0b3JlIHAtdmFsdWUKICAgICAgdGVzdF9yZXN1bHQgPC0gd2lsY294LnRlc3QoZGF0YTEsIGRhdGEyLCBleGFjdCA9IEZBTFNFKQogICAgICBwX3ZhbHVlX21hdHJpeFtpLCBqXSA8LSB0ZXN0X3Jlc3VsdCRwLnZhbHVlCiAgICAgIHBfdmFsdWVfbWF0cml4W2osIGldIDwtIHRlc3RfcmVzdWx0JHAudmFsdWUgICMgTWFrZSBzeW1tZXRyaWMKICAgIH0gZWxzZSB7CiAgICAgIHBfdmFsdWVfbWF0cml4W2ksIGpdIDwtIDEgICMgU2VsZi1jb21wYXJpc29uID0gMQogICAgfQogIH0KfQoKcF92YWx1ZV9tYXRyaXhbaXMubmEocF92YWx1ZV9tYXRyaXgpXSA8LSAxLjAwCnBfdmFsdWVfZGF0YSA8LSBtZWx0KHBfdmFsdWVfbWF0cml4KQpjb2xuYW1lcyhwX3ZhbHVlX2RhdGEpIDwtIGMoImNOMSIsICJjTjIiLCAicF92YWx1ZSIpCnBfdmFsdWVfZGF0YSA8LSBwX3ZhbHVlX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKAogICAgICBwX3ZhbHVlIDwgMC4wMDEgfiAiKioqIiwKICAgICAgcF92YWx1ZSA8IDAuMDEgfiAiKioiLAogICAgICBwX3ZhbHVlIDwgMC4wNSB+ICIqIiwKICAgICAgVFJVRSB+ICIiCiAgICApCiAgKQoKZ2dwbG90KHBfdmFsdWVfZGF0YSwgYWVzKHggPSBjTjEsIHkgPSBjTjIsIGZpbGwgPSBwX3ZhbHVlKSkgKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjgpICsgICMgVGhpY2tlciBncmlkIGxpbmVzIGZvciBzZXBhcmF0aW9uCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNpZ25pZmljYW5jZSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDYsIGZvbnRmYWNlID0gImJvbGQiKSArICAjIFNpZ25pZmljYW5jZSBtYXJrZXJzCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIG1pZHBvaW50ID0gMC4wNSkgKyAgIyBHcmFkaWVudCBjb2xvcnMKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIlBhaXJ3aXNlIFdpbGNveG9uLVRlc3QgUC1WYWx1ZXMgKGN0RE5BLkJhc2UuTVRNIGJ5IGNOKSIsCiAgICAgICB4ID0gIlN0YXR1cyIsIHkgPSAiY04gU3RhdHVzIiwgZmlsbCA9ICJQLVZhbHVlIikKCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGUgPT0gIlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSAhPSAiIixdCmNpcmNfZGF0YSRjVCA8LSBmYWN0b3IoY2lyY19kYXRhJGNULCBsZXZlbHMgPSBjKCJUMCIsICJUMSIsICJUMiIsICJUMyIsICJUNCIpKQpjaXJjX2RhdGEkY04gPC0gZmFjdG9yKGNpcmNfZGF0YSRjTiwgbGV2ZWxzID0gYygiTjAiLCAiTjEiLCAiTjIiLCAiTjMiKSkKY2lyY19kYXRhJGN0RE5BLkJhc2UuTVRNIDwtIGFzLm51bWVyaWMoY2lyY19kYXRhJGN0RE5BLkJhc2UuTVRNKQoKbWVkaWFuX2N0RE5BIDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjVCwgY04pICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3RETkFfQmFzZV9NVE0gPSBtZWRpYW4oY3RETkEuQmFzZS5NVE0sIG5hLnJtID0gVFJVRSkpICU+JQogIHVuZ3JvdXAoKQoKcF92YWx1ZV9tYXRyaXggPC0gZGNhc3QobWVkaWFuX2N0RE5BLCBjVCB+IGNOLCB2YWx1ZS52YXIgPSAibWVkaWFuX2N0RE5BX0Jhc2VfTVRNIikKcF92YWx1ZV9kYXRhIDwtIG1lbHQocF92YWx1ZV9tYXRyaXgsIGlkLnZhcnMgPSAiY1QiLCB2YXJpYWJsZS5uYW1lID0gImNOIiwgdmFsdWUubmFtZSA9ICJtZWRpYW5fdmFsdWUiKQpwX3ZhbHVlX2RhdGEkbWlzc2luZyA8LSBpZmVsc2UoaXMubmEocF92YWx1ZV9kYXRhJG1lZGlhbl92YWx1ZSksICJNaXNzaW5nIiwgIlByZXNlbnQiKQpwX3ZhbHVlX2RhdGEkbWVkaWFuX3ZhbHVlW2lzLm5hKHBfdmFsdWVfZGF0YSRtZWRpYW5fdmFsdWUpXSA8LSAwCgpnZ3Bsb3QocF92YWx1ZV9kYXRhLCBhZXMoeCA9IGNOLCB5ID0gY1QsIGZpbGwgPSBtZWRpYW5fdmFsdWUpKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkgKyAgIyBCbGFjayBncmlkbGluZXMgZm9yIHNlcGFyYXRpb24KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQobWVkaWFuX3ZhbHVlLCAyKSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDUpICsgICMgRGlzcGxheSBtZWRpYW4gdmFsdWVzCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArICAjIENvbG9yIGdyYWRpZW50IHNpbWlsYXIgdG8gdGhlIHJlZmVyZW5jZSBpbWFnZQogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIk1lZGlhbiBjdEROQS5CYXNlLk1UTSBieSBjVCBhbmQgY04iLAogICAgICAgeCA9ICJjTiBTdGF0dXMiLCB5ID0gImNUIFN0YXR1cyIsIGZpbGwgPSAiTWVkaWFuIE1UTSIpICsKICBnZW9tX3RpbGUoZGF0YSA9IHN1YnNldChwX3ZhbHVlX2RhdGEsIG1pc3NpbmcgPT0gIk1pc3NpbmciKSwgCiAgICAgICAgICAgIGFlcyh4ID0gY04sIHkgPSBjVCksIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSAgIyBBZGQgZGlhZ29uYWwgY3Jvc3MgZm9yIG1pc3NpbmcgdmFsdWVzCgpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnRhbGx5KH5jTSwgZGF0YT1jaXJjX2RhdGEsIG1hcmdpbnMgPSBUUlVFKQpjaXJjX2RhdGEkY00gPC0gZmFjdG9yKGNpcmNfZGF0YSRjTSwgbGV2ZWxzID0gYygiTTAiLCJNMSIpLCBsYWJlbHMgPSBjKCJNMCAobj02MSkiLCJNMSAobj0yKSIpKQpib3hwbG90KGN0RE5BLkJhc2UuTVRNfmNNLCBkYXRhPWNpcmNfZGF0YSwgbWFpbj0iY3RETkEgcHJlLXRyZWF0bWVudCBNVE0gLSBjTSIsIHhsYWI9ImNNIiwgeWxhYj0iTVRNL21MIiwgY29sPSJ3aGl0ZSIsYm9yZGVyPSJibGFjayIsIHlsaW0gPSBjKDAsIDUwMCkpCm1lZGlhbl9jdEROQS5jTSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY00pICU+JQogIHN1bW1hcmlzZShtZWRpYW5fY3RETkFfQmFzZV9NVE0gPSBtZWRpYW4oY3RETkEuQmFzZS5NVE0sIG5hLnJtID0gVFJVRSkpCnByaW50KG1lZGlhbl9jdEROQS5jTSkKbTQ8LXdpbGNveC50ZXN0KGN0RE5BLkJhc2UuTVRNIH4gY00sIGRhdGE9Y2lyY19kYXRhLCBuYS5ybT1UUlVFLCBleGFjdD1GQUxTRSwgY29uZi5pbnQ9VFJVRSkKcHJpbnQobTQpCgpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuQmFzZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnRhbGx5KH5wMTYuc3RhdHVzLCBkYXRhPWNpcmNfZGF0YSwgbWFyZ2lucyA9IFRSVUUpCmNpcmNfZGF0YSRwMTYuc3RhdHVzIDwtIGZhY3RvcihjaXJjX2RhdGEkcDE2LnN0YXR1cywgbGV2ZWxzID0gYygiTmVnYXRpdmUiLCJQb3NpdGl2ZSIpLCBsYWJlbHMgPSBjKCJwMTYgbmVnIChuPTI0KSIsInAxNiBwb3MgKG49MzkpIikpCmJveHBsb3QoY3RETkEuQmFzZS5NVE1+cDE2LnN0YXR1cywgZGF0YT1jaXJjX2RhdGEsIG1haW49ImN0RE5BIHByZS10cmVhdG1lbnQgTVRNIC0gcDE2IHN0YXR1cyIsIHhsYWI9InAxNiBzdGF0dXMiLCB5bGFiPSJNVE0vbUwiLCBjb2w9IndoaXRlIixib3JkZXI9ImJsYWNrIiwgeWxpbSA9IGMoMCwgMjAwKSkKbWVkaWFuX2N0RE5BLnAxNiA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkocDE2LnN0YXR1cykgJT4lCiAgc3VtbWFyaXNlKG1lZGlhbl9jdEROQV9CYXNlX01UTSA9IG1lZGlhbihjdEROQS5CYXNlLk1UTSwgbmEucm0gPSBUUlVFKSkKcHJpbnQobWVkaWFuX2N0RE5BLnAxNikKbTU8LXdpbGNveC50ZXN0KGN0RE5BLkJhc2UuTVRNIH4gcDE2LnN0YXR1cywgZGF0YT1jaXJjX2RhdGEsIG5hLnJtPVRSVUUsIGV4YWN0PUZBTFNFLCBjb25mLmludD1UUlVFKQpwcmludChtNSkKCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5CYXNlIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKdGFsbHkoflByaW0uTG9jYXRpb24sIGRhdGE9Y2lyY19kYXRhLCBtYXJnaW5zID0gVFJVRSkKY2lyY19kYXRhJFByaW0uTG9jYXRpb24gPC0gZmFjdG9yKGNpcmNfZGF0YSRQcmltLkxvY2F0aW9uLCBsZXZlbHMgPSBjKCJMYXJ5bngvSHlwb3BoYXJ5bngiLCJPcmFsIGNhdml0eSIsICJPcm9waGFyeW54IiwgIk90aGVyIChwYXJhbmFzYWwgc2ludXMgYW5kIG5hc29waGFyeW5nZWFsKSIpKQpib3hwbG90KGN0RE5BLkJhc2UuTVRNflByaW0uTG9jYXRpb24sIGRhdGE9Y2lyY19kYXRhLCBtYWluPSJjdEROQSBwcmUtdHJlYXRtZW50IE1UTSAtIFR1bW9yIExvY2F0aW9uIiwgeGxhYj0iVHVtb3IgTG9jYXRpb24iLCB5bGFiPSJNVE0vbUwiLCBjb2w9IndoaXRlIixib3JkZXI9ImJsYWNrIiwgeWxpbSA9IGMoMCwgMjAwKSkKbWVkaWFuX2N0RE5BLmxvYyA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoUHJpbS5Mb2NhdGlvbikgJT4lCiAgc3VtbWFyaXNlKG1lZGlhbl9jdEROQV9CYXNlX01UTSA9IG1lZGlhbihjdEROQS5CYXNlLk1UTSwgbmEucm0gPSBUUlVFKSkKcHJpbnQobWVkaWFuX2N0RE5BLmxvYykKcGFpcndpc2Vfd2lsY294IDwtIHBhaXJ3aXNlLndpbGNveC50ZXN0KGNpcmNfZGF0YSRjdEROQS5CYXNlLk1UTSwgY2lyY19kYXRhJFByaW0uTG9jYXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4YWN0ID0gRkFMU0UpCgpwcmludChwYWlyd2lzZV93aWxjb3gpCgpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlID09ICJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLkJhc2UgIT0gIiIsXQpjaXJjX2RhdGEkUHJpbS5Mb2NhdGlvbiA8LSBmYWN0b3IoY2lyY19kYXRhJFByaW0uTG9jYXRpb24sIGxldmVscyA9IGMoIkxhcnlueC9IeXBvcGhhcnlueCIsIk9yYWwgY2F2aXR5IiwgIk9yb3BoYXJ5bngiLCAiT3RoZXIgKHBhcmFuYXNhbCBzaW51cyBhbmQgbmFzb3BoYXJ5bmdlYWwpIiksIGxhYmVscyA9IGMoIkxSWC9IUFJYIiwiT0MiLCJQUlgiLCJPdGhlciIpKQpjaXJjX2RhdGEkY3RETkEuQmFzZS5NVE0gPC0gYXMubnVtZXJpYyhjaXJjX2RhdGEkY3RETkEuQmFzZS5NVE0pCnBsX2xldmVscyA8LSBsZXZlbHMoY2lyY19kYXRhJFByaW0uTG9jYXRpb24pCnBfdmFsdWVfbWF0cml4IDwtIG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aChwbF9sZXZlbHMpLCBuY29sID0gbGVuZ3RoKHBsX2xldmVscykpCnJvd25hbWVzKHBfdmFsdWVfbWF0cml4KSA8LSBwbF9sZXZlbHMKY29sbmFtZXMocF92YWx1ZV9tYXRyaXgpIDwtIHBsX2xldmVscwoKZm9yIChpIGluIDE6bGVuZ3RoKHBsX2xldmVscykpIHsKICBmb3IgKGogaW4gaTpsZW5ndGgocGxfbGV2ZWxzKSkgewogICAgaWYgKGkgIT0gaikgewogICAgICAjIEV4dHJhY3QgZGF0YSBmb3IgYm90aCBncm91cHMKICAgICAgZGF0YTEgPC0gY2lyY19kYXRhICU+JSBmaWx0ZXIoUHJpbS5Mb2NhdGlvbiA9PSBwbF9sZXZlbHNbaV0pICU+JSBwdWxsKGN0RE5BLkJhc2UuTVRNKQogICAgICBkYXRhMiA8LSBjaXJjX2RhdGEgJT4lIGZpbHRlcihQcmltLkxvY2F0aW9uID09IHBsX2xldmVsc1tqXSkgJT4lIHB1bGwoY3RETkEuQmFzZS5NVE0pCiAgICAgIAogICAgICAjIFBlcmZvcm0gV2lsY294b24gdGVzdCBhbmQgc3RvcmUgcC12YWx1ZQogICAgICB0ZXN0X3Jlc3VsdCA8LSB3aWxjb3gudGVzdChkYXRhMSwgZGF0YTIsIGV4YWN0ID0gRkFMU0UpCiAgICAgIHBfdmFsdWVfbWF0cml4W2ksIGpdIDwtIHRlc3RfcmVzdWx0JHAudmFsdWUKICAgICAgcF92YWx1ZV9tYXRyaXhbaiwgaV0gPC0gdGVzdF9yZXN1bHQkcC52YWx1ZSAgIyBNYWtlIHN5bW1ldHJpYwogICAgfSBlbHNlIHsKICAgICAgcF92YWx1ZV9tYXRyaXhbaSwgal0gPC0gMSAgIyBTZWxmLWNvbXBhcmlzb24gPSAxCiAgICB9CiAgfQp9CgpwX3ZhbHVlX21hdHJpeFtpcy5uYShwX3ZhbHVlX21hdHJpeCldIDwtIDEuMDAKcF92YWx1ZV9kYXRhIDwtIG1lbHQocF92YWx1ZV9tYXRyaXgpCmNvbG5hbWVzKHBfdmFsdWVfZGF0YSkgPC0gYygicGwxIiwgInBsMiIsICJwX3ZhbHVlIikKcF92YWx1ZV9kYXRhIDwtIHBfdmFsdWVfZGF0YSAlPiUKICBtdXRhdGUoCiAgICBzaWduaWZpY2FuY2UgPSBjYXNlX3doZW4oCiAgICAgIHBfdmFsdWUgPCAwLjAwMSB+ICIqKioiLAogICAgICBwX3ZhbHVlIDwgMC4wMSB+ICIqKiIsCiAgICAgIHBfdmFsdWUgPCAwLjA1IH4gIioiLAogICAgICBUUlVFIH4gIiIKICAgICkKICApCgpnZ3Bsb3QocF92YWx1ZV9kYXRhLCBhZXMoeCA9IHBsMSwgeSA9IHBsMiwgZmlsbCA9IHBfdmFsdWUpKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuOCkgKyAgIyBUaGlja2VyIGdyaWQgbGluZXMgZm9yIHNlcGFyYXRpb24KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2lnbmlmaWNhbmNlKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNiwgZm9udGZhY2UgPSAiYm9sZCIpICsgICMgU2lnbmlmaWNhbmNlIG1hcmtlcnMKICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIiwgbWlkcG9pbnQgPSAwLjA1KSArICAjIEdyYWRpZW50IGNvbG9ycwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAiUGFpcndpc2UgV2lsY294b24tVGVzdCBQLVZhbHVlcyAoY3RETkEuQmFzZS5NVE0gYnkgVHVtb3IgTG9jYXRpb24pIiwKICAgICAgIHggPSAiVHVtb3IgTG9jYXRpb24iLCB5ID0gIlR1bW9yIExvY2F0aW9uIiwgZmlsbCA9ICJQLVZhbHVlIikKYGBgCgojUEZTIGJ5IGN0RE5BIHN0YXR1cyBhdCBNUkQKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuTVJEKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShQRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUEZTIC0gY3RETkEgYXQgTVJEIiwgeWxhYj0gIlByb2dyZXNzaW9uLUZyZWUgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBEZWZpbml0aXZlIFRyZWF0bWVudCIsIGxlZ2VuZC5sYWJzPWMoImN0RE5BIE5lZ2F0aXZlIiwgImN0RE5BIFBvc2l0aXZlIiksIGxlZ2VuZC50aXRsZT0iIikKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMCwgMTIsIDI0LCAzNikpCmNpcmNfZGF0YSRjdEROQS5NUkQgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5NUkQsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCxkYXRhID0gY2lyY19kYXRhKSAKc3VtbWFyeShjb3hfZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShjb3hfZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKCmNpcmNfZGF0YSRjdEROQS5NUkQgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5NUkQsIGxldmVscyA9IGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRQRlMuRXZlbnQgPC0gZmFjdG9yKGNpcmNfZGF0YSRQRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUHJvZ3Jlc3Npb24iLCAiUHJvZ3Jlc3Npb24iKSkKY29udGluZ2VuY3lfdGFibGUgPC0gdGFibGUoY2lyY19kYXRhJGN0RE5BLk1SRCwgY2lyY19kYXRhJFBGUy5FdmVudCkKY2hpX3NxdWFyZV90ZXN0IDwtIGNoaXNxLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGNoaV9zcXVhcmVfdGVzdCkKZmlzaGVyX2V4YWN0X3Rlc3QgPC0gZmlzaGVyLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGZpc2hlcl9leGFjdF90ZXN0KQpwcmludChjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYgPC0gYXMuZGF0YS5mcmFtZShjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYkVG90YWwgPC0gYXZlKHRhYmxlX2RmJEZyZXEsIHRhYmxlX2RmJFZhcjEsIEZVTiA9IHN1bSkKdGFibGVfZGYkUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRGcmVxIC8gdGFibGVfZGYkVG90YWwKdGFibGVfZGYkTWlkZGxlUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRQZXJjZW50YWdlIC8gMgpnZ3Bsb3QodGFibGVfZGYsIGFlcyh4ID0gVmFyMSwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBWYXIyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gTWlkZGxlUGVyY2VudGFnZSwgbGFiZWwgPSBGcmVxKSwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJibGFjayIsIHZqdXN0ID0gMS41LCBzaXplID0gNykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJjdEROQSBzdGF0dXMgYXQgTVJEIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNPUyBieSBjdEROQSBzdGF0dXMgYXQgTVJECmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkT1MubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRPUy5FdmVudCl+Y3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhKQpldmVudF9zdW1tYXJ5IDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjdEROQS5NUkQpICU+JQogIHN1bW1hcmlzZSgKICAgIFRvdGFsID0gbigpLAogICAgRXZlbnRzID0gc3VtKE9TLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRPUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJE9TLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhLGNvbmYuaW50PTAuOTUsY29uZi50eXBlPSJsb2ctbG9nIikgCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJPUyAtIGN0RE5BIGF0IE1SRCIsIHlsYWI9ICJPdmVyYWxsIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkT1MuRXZlbnQgPC0gZmFjdG9yKGNpcmNfZGF0YSRPUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJBbGl2ZSIsICJEZWNlYXNlZCIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkT1MuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IE1SRCIsIAogICAgICAgeCA9ICJjdEROQSIsIAogICAgICAgeSA9ICJQYXRpZW50cyAoJSkiLCAKICAgICAgIGZpbGwgPSAiTGl2aW5nIFN0YXR1cyIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkFsaXZlIiA9ICJibHVlIiwgIkRlY2Vhc2VkIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNQRlMgYnkgY3RETkEgc3RhdHVzIGF0IE1SRCAtIGV4Y2x1ZGUgcHRzIHdpdGggYWRqdXZhbnQgdHJlYXRtZW50IHBvc3QtTVJECmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEIT0iIixdCmV4Y2x1ZGVkX2lkcyA8LSBjKCJVTk0tMDA3IiwgIlVOTS0wMDgiLCAiVU5NLTAyMyIsICJVTk0tMDI3IiwgIlVOTS0wMjkiLCAKICAgICAgICAgICAgICAgICAgIlVOTS0wMzAiLCAiVU5NLTAzNSIsICJVTk0tMDQ1IiwgIlVOTS0wNTEiLCAiVU5NLTA1OSIsIAogICAgICAgICAgICAgICAgICAiVU5NLTA3NSIsICJVTk0tMDgyIiwgIlVOTS0wMzIiLCAiVU5NLTA0MiIsICJVTk0tMDQzIiwgCiAgICAgICAgICAgICAgICAgICJVTk0tMDQ4IiwgIlVOTS0wNTAiLCAiVU5NLTA3MCIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbIWNpcmNfZGF0YSRQYXRpZW50SUQgJWluJSBleGNsdWRlZF9pZHMsIF0KCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCl+Y3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhKQpldmVudF9zdW1tYXJ5IDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjdEROQS5NUkQpICU+JQogIHN1bW1hcmlzZSgKICAgIFRvdGFsID0gbigpLAogICAgRXZlbnRzID0gc3VtKFBGUy5FdmVudCksCiAgICBGcmFjdGlvbiA9IEV2ZW50cyAvIG4oKSwKICAgIFBlcmNlbnRhZ2UgPSAoRXZlbnRzIC8gbigpKSAqIDEwMAogICkKcHJpbnQoZXZlbnRfc3VtbWFyeSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhLGNvbmYuaW50PTAuOTUsY29uZi50eXBlPSJsb2ctbG9nIikgCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJQRlMgLSBjdEROQSBhdCBNUkQiLCB5bGFiPSAiUHJvZ3Jlc3Npb24tRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIERlZmluaXRpdmUgVHJlYXRtZW50IiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMjQsIDM2KSkKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQoKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzID0gYygiTkVHQVRJVkUiLCAiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJFBGUy5FdmVudCA8LSBmYWN0b3IoY2lyY19kYXRhJFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkUEZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBNUkQiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlByb2dyZXNzaW9uIiwKICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiRmlzaGVyJ3MgZXhhY3QgdGVzdCBwLXZhbHVlOiAiLCBmb3JtYXQucHZhbChmaXNoZXJfZXhhY3RfdGVzdCRwLnZhbHVlKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gUHJvZ3Jlc3Npb24iID0gImJsdWUiLCAiUHJvZ3Jlc3Npb24iID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBQcm9ncmVzc2lvbiBsYWJlbCBzaXplCmBgYAoKI09TIGJ5IGN0RE5BIHN0YXR1cyBhdCBNUkQgLSBleGNsdWRlIHB0cyB3aXRoIGFkanV2YW50IHRyZWF0bWVudCBwb3N0LU1SRApgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCE9IiIsXQpleGNsdWRlZF9pZHMgPC0gYygiVU5NLTAwNyIsICJVTk0tMDA4IiwgIlVOTS0wMjMiLCAiVU5NLTAyNyIsICJVTk0tMDI5IiwgCiAgICAgICAgICAgICAgICAgICJVTk0tMDMwIiwgIlVOTS0wMzUiLCAiVU5NLTA0NSIsICJVTk0tMDUxIiwgIlVOTS0wNTkiLCAKICAgICAgICAgICAgICAgICAgIlVOTS0wNzUiLCAiVU5NLTA4MiIsICJVTk0tMDMyIiwgIlVOTS0wNDIiLCAiVU5NLTA0MyIsIAogICAgICAgICAgICAgICAgICAiVU5NLTA0OCIsICJVTk0tMDUwIiwgIlVOTS0wNzAiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhWyFjaXJjX2RhdGEkUGF0aWVudElEICVpbiUgZXhjbHVkZWRfaWRzLCBdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRPUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJE9TLkV2ZW50KX5jdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLk1SRCkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oT1MuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJE9TLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkT1MuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9Ik9TIC0gY3RETkEgYXQgTVJEIiwgeWxhYj0gIk92ZXJhbGwgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBEZWZpbml0aXZlIFRyZWF0bWVudCIsIGxlZ2VuZC5sYWJzPWMoImN0RE5BIE5lZ2F0aXZlIiwgImN0RE5BIFBvc2l0aXZlIiksIGxlZ2VuZC50aXRsZT0iIikKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMTIsIDI0LCAzNikpCmNpcmNfZGF0YSRjdEROQS5NUkQgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5NUkQsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCxkYXRhID0gY2lyY19kYXRhKSAKc3VtbWFyeShjb3hfZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShjb3hfZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKCmNpcmNfZGF0YSRjdEROQS5NUkQgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5NUkQsIGxldmVscyA9IGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRPUy5FdmVudCA8LSBmYWN0b3IoY2lyY19kYXRhJE9TLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIkFsaXZlIiwgIkRlY2Vhc2VkIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5NUkQsIGNpcmNfZGF0YSRPUy5FdmVudCkKY2hpX3NxdWFyZV90ZXN0IDwtIGNoaXNxLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGNoaV9zcXVhcmVfdGVzdCkKZmlzaGVyX2V4YWN0X3Rlc3QgPC0gZmlzaGVyLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGZpc2hlcl9leGFjdF90ZXN0KQpwcmludChjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYgPC0gYXMuZGF0YS5mcmFtZShjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYkVG90YWwgPC0gYXZlKHRhYmxlX2RmJEZyZXEsIHRhYmxlX2RmJFZhcjEsIEZVTiA9IHN1bSkKdGFibGVfZGYkUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRGcmVxIC8gdGFibGVfZGYkVG90YWwKdGFibGVfZGYkTWlkZGxlUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRQZXJjZW50YWdlIC8gMgpnZ3Bsb3QodGFibGVfZGYsIGFlcyh4ID0gVmFyMSwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBWYXIyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gTWlkZGxlUGVyY2VudGFnZSwgbGFiZWwgPSBGcmVxKSwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJibGFjayIsIHZqdXN0ID0gMS41LCBzaXplID0gNykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJjdEROQSBzdGF0dXMgYXQgTVJEIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJMaXZpbmcgU3RhdHVzIiwKICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiRmlzaGVyJ3MgZXhhY3QgdGVzdCBwLXZhbHVlOiAiLCBmb3JtYXQucHZhbChmaXNoZXJfZXhhY3RfdGVzdCRwLnZhbHVlKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQWxpdmUiID0gImJsdWUiLCAiRGVjZWFzZWQiID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBQcm9ncmVzc2lvbiBsYWJlbCBzaXplCmBgYAoKI1BGUyBieSBjdEROQSBzdGF0dXMgYXQgTVJEIFN0YWdlIEkvSUkKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjU3RhZ2U9PSJJL0lJIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCl+Y3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhKQpldmVudF9zdW1tYXJ5IDwtIGNpcmNfZGF0YSAlPiUKICBncm91cF9ieShjdEROQS5NUkQpICU+JQogIHN1bW1hcmlzZSgKICAgIFRvdGFsID0gbigpLAogICAgRXZlbnRzID0gc3VtKFBGUy5FdmVudCksCiAgICBGcmFjdGlvbiA9IEV2ZW50cyAvIG4oKSwKICAgIFBlcmNlbnRhZ2UgPSAoRXZlbnRzIC8gbigpKSAqIDEwMAogICkKcHJpbnQoZXZlbnRfc3VtbWFyeSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KQpLTV9jdXJ2ZSA8LSBzdXJ2Zml0KHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhID0gY2lyY19kYXRhLGNvbmYuaW50PTAuOTUsY29uZi50eXBlPSJsb2ctbG9nIikgCmdnc3VydnBsb3QoS01fY3VydmUsIGRhdGEgPSBjaXJjX2RhdGEsIHB2YWwgPSBGQUxTRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJQRlMgLSBjdEROQSBhdCBNUkQgU3RhZ2UgSS9JSSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5NUkQsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IE1SRCBTdGFnZSBJL0lJIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNQRlMgYnkgY3RETkEgc3RhdHVzIGF0IE1SRCBTdGFnZSBJSUkvSVYKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjU3RhZ2U9PSJJSUkvSVYiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KX5jdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLk1SRCkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oUEZTLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlBGUyAtIGN0RE5BIGF0IE1SRCBTdGFnZSBJSUkvSVYiLCB5bGFiPSAiUHJvZ3Jlc3Npb24tRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIERlZmluaXRpdmUgVHJlYXRtZW50IiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMjQsIDM2KSkKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQoKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzID0gYygiTkVHQVRJVkUiLCAiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJFBGUy5FdmVudCA8LSBmYWN0b3IoY2lyY19kYXRhJFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkUEZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBNUkQgU3RhZ2UgSS9JSSIsIAogICAgICAgeCA9ICJjdEROQSIsIAogICAgICAgeSA9ICJQYXRpZW50cyAoJSkiLCAKICAgICAgIGZpbGwgPSAiUHJvZ3Jlc3Npb24iLAogICAgICAgY2FwdGlvbiA9IHBhc3RlKCJGaXNoZXIncyBleGFjdCB0ZXN0IHAtdmFsdWU6ICIsIGZvcm1hdC5wdmFsKGZpc2hlcl9leGFjdF90ZXN0JHAudmFsdWUpKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJObyBQcm9ncmVzc2lvbiIgPSAiYmx1ZSIsICJQcm9ncmVzc2lvbiIgPSAicmVkIikpICsgIyBkZWZpbmUgY3VzdG9tIGNvbG9ycwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxLjUsIHNpemUgPSAxNCksICMgaW5jcmVhc2UgeC1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB4LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIGxhYmVsIHNpemUKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikpICAjIGluY3JlYXNlIFByb2dyZXNzaW9uIGxhYmVsIHNpemUKYGBgCgojUEZTIGJ5IGN0RE5BIGF0IE1SRCBwMTYoKykKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRwMTYuc3RhdHVzPT0iUG9zaXRpdmUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KX5jdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLk1SRCkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oUEZTLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlBGUyAtIGN0RE5BIGF0IE1SRCBwMTYoKykiLCB5bGFiPSAiUHJvZ3Jlc3Npb24tRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIERlZmluaXRpdmUgVHJlYXRtZW50IiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMjQsIDM2KSkKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQoKY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzID0gYygiTkVHQVRJVkUiLCAiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJFBGUy5FdmVudCA8LSBmYWN0b3IoY2lyY19kYXRhJFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkUEZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBNUkQgcDE2KCspIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNQRlMgYnkgY3RETkEgYXQgTVJEIHAxNigtKQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJHAxNi5zdGF0dXM9PSJOZWdhdGl2ZSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuTVJEKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShQRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUEZTIC0gY3RETkEgYXQgTVJEIHAxNigtKSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5NUkQsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IE1SRCBwMTYoLSkiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlByb2dyZXNzaW9uIiwKICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiRmlzaGVyJ3MgZXhhY3QgdGVzdCBwLXZhbHVlOiAiLCBmb3JtYXQucHZhbChmaXNoZXJfZXhhY3RfdGVzdCRwLnZhbHVlKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gUHJvZ3Jlc3Npb24iID0gImJsdWUiLCAiUHJvZ3Jlc3Npb24iID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBQcm9ncmVzc2lvbiBsYWJlbCBzaXplCmBgYAoKI1BGUyBieSBjdEROQSBzdGF0dXMgYXQgc3VydmVpbGxhbmNlCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KX5jdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLlN1cnZlaWxsYW5jZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oUEZTLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlBGUyAtIGN0RE5BIGF0IFN1cnZlaWxsYW5jZSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuU3VydmVpbGxhbmNlLCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSIsIAogICAgICAgeCA9ICJjdEROQSIsIAogICAgICAgeSA9ICJQYXRpZW50cyAoJSkiLCAKICAgICAgIGZpbGwgPSAiUHJvZ3Jlc3Npb24iLAogICAgICAgY2FwdGlvbiA9IHBhc3RlKCJGaXNoZXIncyBleGFjdCB0ZXN0IHAtdmFsdWU6ICIsIGZvcm1hdC5wdmFsKGZpc2hlcl9leGFjdF90ZXN0JHAudmFsdWUpKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJObyBQcm9ncmVzc2lvbiIgPSAiYmx1ZSIsICJQcm9ncmVzc2lvbiIgPSAicmVkIikpICsgIyBkZWZpbmUgY3VzdG9tIGNvbG9ycwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxLjUsIHNpemUgPSAxNCksICMgaW5jcmVhc2UgeC1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB4LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIGxhYmVsIHNpemUKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikpICAjIGluY3JlYXNlIFByb2dyZXNzaW9uIGxhYmVsIHNpemUKYGBgCgojT1MgYnkgY3RETkEgc3RhdHVzIGF0IHN1cnZlaWxsYW5jZQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJE9TLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkT1MuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShPUy5FdmVudCksCiAgICBGcmFjdGlvbiA9IEV2ZW50cyAvIG4oKSwKICAgIFBlcmNlbnRhZ2UgPSAoRXZlbnRzIC8gbigpKSAqIDEwMAogICkKcHJpbnQoZXZlbnRfc3VtbWFyeSkKc3Vydl9vYmplY3QgPC1TdXJ2KHRpbWUgPSBjaXJjX2RhdGEkT1MubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRPUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iT1MgLSBjdEROQSBhdCBTdXJ2ZWlsbGFuY2UiLCB5bGFiPSAiT3ZlcmFsbCBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIERlZmluaXRpdmUgVHJlYXRtZW50IiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMjQsIDM2KSkKY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCiNNZWRpYW4gbnVtYmVycyBvZiB0aW1lIHBvaW50cyBpbiB0aGUgbG9uZ2l0dWRpbmFsIHNldHRpbmcKYGBge3J9CiMgTG9hZCB0aGUgZGF0YXNldApybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSE9IiIsXQpjaXJjX2RhdGFkZiA8LSBhcy5kYXRhLmZyYW1lKGNpcmNfZGF0YSkKCm1lZGlhbl9Oc3VydnRwcyA8LSBtZWRpYW4oY2lyY19kYXRhZGYkTnN1cnZ0cHMsIG5hLnJtID0gVFJVRSkKbWluX05zdXJ2dHBzIDwtIG1pbihjaXJjX2RhdGFkZiROc3VydnRwcywgbmEucm0gPSBUUlVFKQptYXhfTnN1cnZ0cHMgPC0gbWF4KGNpcmNfZGF0YWRmJE5zdXJ2dHBzLCBuYS5ybSA9IFRSVUUpCgpjYXQoc3ByaW50ZigiTWVkaWFuICMgb2Ygc3VydmVpbGxhbmNlIHRpbWUgcG9pbnRzOiAlZCAoJWQtJWQpXG4iLCAKICAgICAgICAgICAgbWVkaWFuX05zdXJ2dHBzLCBtaW5fTnN1cnZ0cHMsIG1heF9Oc3VydnRwcykpCmBgYAoKI1RpbWUtZGVwZW5kZW50IGFuYWx5c2lzIGZvciBQRlMgaW4gbG9uZ2l0dWRpbmFsIHRpbWUgcG9pbnRzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmR0IDwtIHJlYWRfeGxzeCgiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGFfVGltZSBkZXBlbmRlbnQueGxzeCIpIHw+CiAgY2xlYW5fbmFtZXMoKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBjKHdpbmRvd19zdGFydF9kYXRlLGRmc19kYXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEzX2RhdGUpLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoYXMuRGF0ZSgueCwgZm9ybWF0ID0gIiVZLSVtLSVkIikpKSkKCmR0X2Jpb21hcmtlciA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfc3RhdHVzOnN1cnZlaWxsYW5jZV8xM19kYXRlKSB8PgogIGZpbHRlcihjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSkgfD4KICBwaXZvdF9sb25nZXIoY29scyA9IHN1cnZlaWxsYW5jZV8xX3N0YXR1czpzdXJ2ZWlsbGFuY2VfMTNfZGF0ZSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJ2aXNpdF9udW1iZXIiLCAiLnZhbHVlIiksCiAgICAgICAgICAgICAgIG5hbWVzX3BhdHRlcm4gPSAic3VydmVpbGxhbmNlXyguKV8oLiopIikgfD4KICBtdXRhdGUoYmlvbWFya2VyX3RpbWUgPSBkYXkoZGF5cyhkYXRlIC0gd2luZG93X3N0YXJ0X2RhdGUpKSkgfD4KICBzZWxlY3QocHRzX2lkLCBiaW9tYXJrZXJfdGltZSwgYmlvbWFya2VyX3N0YXR1cyA9IHN0YXR1cykgfD4KICBmaWx0ZXIoIWlzLm5hKGJpb21hcmtlcl90aW1lKSkKCmdsaW1wc2UoZHRfYmlvbWFya2VyKQoKZHRfc3Vydml2YWwgPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGU6ZGZzX2RhdGUsIGRmc19ldmVudCkgfD4gICMgQWRkZWQgZGZzX2V2ZW50IGhlcmUKICBmaWx0ZXIoY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUpIHw+CiAgbXV0YXRlKGRmc190aW1lID0gKGRmc19kYXRlIC0gd2luZG93X3N0YXJ0X2RhdGUpLAogICAgICAgICBkZnNfdGltZSA9IGRheShkYXlzKGRmc190aW1lKSksCiAgICAgICAgIGRmc19ldmVudCA9IGFzLm51bWVyaWMoZGZzX2V2ZW50KSkgfD4KICBzZWxlY3QocHRzX2lkLCBkZnNfdGltZSwgZGZzX2V2ZW50KQoKZ2xpbXBzZShkdF9zdXJ2aXZhbCkKCmF1eCA8LSBkdF9zdXJ2aXZhbCAlPiUgCiAgZmlsdGVyKGRmc190aW1lIDw9IDApCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIGRmc190aW1lLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSB8PgogIHNlbGVjdChwdHNfaWQsIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwgZGZzX3RpbWUpCgpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmR0X3N1cnZpdmFsIDwtIGR0X3N1cnZpdmFsIHw+CiAgZmlsdGVyKGRmc190aW1lID4gMCkKCmF1eCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggLSB3aW5kb3dfc3RhcnRfZGF0ZSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggPCAwKSkgfD4KICByb3d3aXNlKCkgfD4KICBtdXRhdGUoc3VtX25lZyA9IAogICAgICAgICAgIHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSwKICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZykKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBmaWx0ZXIoc3VtX25lZyA+IDApIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZywgd2luZG93X3N0YXJ0X2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHdpbmRvd19zdGFydF9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgCgpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmF1eCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGRmc19kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IC0gd2luZG93X3N0YXJ0X2RhdGUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMl9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLAogICAgICAgICAgICAgICAgLmZucyA9IH4gZGZzX2RhdGUgPCAueCkpIHw+CiAgcm93d2lzZSgpIHw+CiAgbXV0YXRlKG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50ID0gc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8yX2RhdGU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VydmVpbGxhbmNlXzE0X2RhdGUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwKICAgICAgICAgICAgICAgIC5mbnMgPSB+ICFpcy5uYSgueCkpKSB8PgogIG11dGF0ZSh0b3RhbF9iaW9tYXJrZXIgPSBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzJfZGF0ZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VydmVpbGxhbmNlXzE0X2RhdGUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgbl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlcikKCnRlbXAgPC0gYXV4IHw+IAogIHNlbGVjdCgtcHRzX2lkKSB8PiAKICBncm91cF9ieShuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyKSB8PiAgIyBEaXJlY3QgZ3JvdXBpbmcKICBzdW1tYXJpc2UoZnJlcSA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikgICMgRHJvcCBncm91cHMgYWZ0ZXIgc3VtbWFyaXphdGlvbgoKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBzZWxlY3QocHRzX2lkLCBuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyLCAKICAgICAgICAgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8yX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IGRmc19kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgfD4KICBmaWx0ZXIobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQgPiAwKQpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmF1eCA8LSB0bWVyZ2UoZGF0YTEgPSBkdF9zdXJ2aXZhbCwgCiAgICAgICAgICAgICAgZGF0YTIgPSBkdF9zdXJ2aXZhbCwKICAgICAgICAgICAgICBpZCA9IHB0c19pZCwgCiAgICAgICAgICAgICAgZGZzX2V2ZW50ID0gZXZlbnQoZGZzX3RpbWUsIGRmc19ldmVudCkpCmR0X2ZpbmFsIDwtIHRtZXJnZShkYXRhMSA9IGF1eCwgCiAgICAgICAgICAgICAgICAgICBkYXRhMiA9IGR0X2Jpb21hcmtlciwKICAgICAgICAgICAgICAgICAgIGlkID0gcHRzX2lkLCAKICAgICAgICAgICAgICAgICAgIGJpb21hcmtlcl9zdGF0dXMgPSAKICAgICAgICAgICAgICAgICAgICAgdGRjKGJpb21hcmtlcl90aW1lLCBiaW9tYXJrZXJfc3RhdHVzKSkKCmRhdGF0YWJsZShkdF9maW5hbCwgZmlsdGVyID0gInRvcCIpCgojIFN5bnRheCBpZiB0aGVyZSBpcyBub3QgdGltZS1kZXBlbmRlbnQgY292YXJpYXRlCiMgZml0IDwtIGNveHBoKFN1cnYoZGZzX3RpbWUsIGRmc19ldmVudCkgfiBiaW9tYXJrZXJfc3RhdHVzLAojICAgICAgICAgICAgICBkYXRhID0gZHRfZmluYWwpCiMgc3VtbWFyeShmaXQpCgpmaXQgPC0gY294cGgoU3Vydih0c3RhcnQsIHRzdG9wLCBkZnNfZXZlbnQpIH4gYmlvbWFya2VyX3N0YXR1cywKICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKc3VtbWFyeShmaXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCmBgYAoKCiNUaW1lLWRlcGVuZGVudCBhbmFseXNpcyBmb3IgT1MgaW4gbG9uZ2l0dWRpbmFsIHRpbWUgcG9pbnRzCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmR0IDwtIHJlYWRfeGxzeCgiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGFfVGltZSBkZXBlbmRlbnQueGxzeCIpIHw+CiAgY2xlYW5fbmFtZXMoKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBjKHdpbmRvd19zdGFydF9kYXRlLG9zX2RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTNfZGF0ZSksIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZShhcy5EYXRlKC54LCBmb3JtYXQgPSAiJVktJW0tJWQiKSkpKQoKZHRfYmlvbWFya2VyIDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9zdGF0dXM6c3VydmVpbGxhbmNlXzEzX2RhdGUpIHw+CiAgZmlsdGVyKGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlKSB8PgogIHBpdm90X2xvbmdlcihjb2xzID0gc3VydmVpbGxhbmNlXzFfc3RhdHVzOnN1cnZlaWxsYW5jZV8xM19kYXRlLAogICAgICAgICAgICAgICBuYW1lc190byA9IGMoInZpc2l0X251bWJlciIsICIudmFsdWUiKSwKICAgICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICJzdXJ2ZWlsbGFuY2VfKC4pXyguKikiKSB8PgogIG11dGF0ZShiaW9tYXJrZXJfdGltZSA9IGRheShkYXlzKGRhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSkpKSB8PgogIHNlbGVjdChwdHNfaWQsIGJpb21hcmtlcl90aW1lLCBiaW9tYXJrZXJfc3RhdHVzID0gc3RhdHVzKSB8PgogIGZpbHRlcighaXMubmEoYmlvbWFya2VyX3RpbWUpKQoKZ2xpbXBzZShkdF9iaW9tYXJrZXIpCgpkdF9zdXJ2aXZhbCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZTpvc19kYXRlLCBvc19ldmVudCkgfD4gICMgQWRkZWQgb3NfZXZlbnQgaGVyZQogIGZpbHRlcihjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSkgfD4KICBtdXRhdGUoZGZzX3RpbWUgPSAob3NfZGF0ZSAtIHdpbmRvd19zdGFydF9kYXRlKSwKICAgICAgICAgZGZzX3RpbWUgPSBkYXkoZGF5cyhkZnNfdGltZSkpLAogICAgICAgICBvc19ldmVudCA9IGFzLm51bWVyaWMob3NfZXZlbnQpKSB8PgogIHNlbGVjdChwdHNfaWQsIGRmc190aW1lLCBvc19ldmVudCkKCmdsaW1wc2UoZHRfc3Vydml2YWwpCgphdXggPC0gZHRfc3Vydml2YWwgJT4lIAogIGZpbHRlcihkZnNfdGltZSA8PSAwKQoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIHNlbGVjdChwdHNfaWQsIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfdGltZSwgb3NfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gb3NfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIG9zX2RhdGUsIGRmc190aW1lKQoKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgpkdF9zdXJ2aXZhbCA8LSBkdF9zdXJ2aXZhbCB8PgogIGZpbHRlcihkZnNfdGltZSA+IDApCgphdXggPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IC0gd2luZG93X3N0YXJ0X2RhdGUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IC54IDwgMCkpIHw+CiAgcm93d2lzZSgpIHw+CiAgbXV0YXRlKHN1bV9uZWcgPSAKICAgICAgICAgICBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSksCiAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpICB8PgogIHNlbGVjdChwdHNfaWQsIHN1bV9uZWcpCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgZmlsdGVyKHN1bV9uZWcgPiAwKSB8PgogIHNlbGVjdChwdHNfaWQsIHN1bV9uZWcsIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSB3aW5kb3dfc3RhcnRfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIAoKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgphdXggPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCAtIHdpbmRvd19zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGRmc19kYXRlIDwgLngpKSB8PgogIHJvd3dpc2UoKSB8PgogIG11dGF0ZShuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA9IHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMl9kYXRlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xNF9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsCiAgICAgICAgICAgICAgICAuZm5zID0gfiAhaXMubmEoLngpKSkgfD4KICBtdXRhdGUodG90YWxfYmlvbWFya2VyID0gc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8yX2RhdGU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xNF9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpCgp0ZW1wIDwtIGF1eCB8PiAKICBzZWxlY3QoLXB0c19pZCkgfD4gCiAgZ3JvdXBfYnkobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlcikgfD4gICMgRGlyZWN0IGdyb3VwaW5nCiAgc3VtbWFyaXNlKGZyZXEgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICAjIERyb3AgZ3JvdXBzIGFmdGVyIHN1bW1hcml6YXRpb24KCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgc2VsZWN0KHB0c19pZCwgbl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlciwgCiAgICAgICAgIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMl9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgZmlsdGVyKG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50ID4gMCkKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgphdXggPC0gdG1lcmdlKGRhdGExID0gZHRfc3Vydml2YWwsIAogICAgICAgICAgICAgIGRhdGEyID0gZHRfc3Vydml2YWwsCiAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgIG9zX2V2ZW50ID0gZXZlbnQoZGZzX3RpbWUsIG9zX2V2ZW50KSkKZHRfZmluYWwgPC0gdG1lcmdlKGRhdGExID0gYXV4LCAKICAgICAgICAgICAgICAgICAgIGRhdGEyID0gZHRfYmlvbWFya2VyLAogICAgICAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgICAgICAgYmlvbWFya2VyX3N0YXR1cyA9IAogICAgICAgICAgICAgICAgICAgICB0ZGMoYmlvbWFya2VyX3RpbWUsIGJpb21hcmtlcl9zdGF0dXMpKQoKZGF0YXRhYmxlKGR0X2ZpbmFsLCBmaWx0ZXIgPSAidG9wIikKCiMgU3ludGF4IGlmIHRoZXJlIGlzIG5vdCB0aW1lLWRlcGVuZGVudCBjb3ZhcmlhdGUKIyBmaXQgPC0gY294cGgoU3VydihkZnNfdGltZSwgb3NfZXZlbnQpIH4gYmlvbWFya2VyX3N0YXR1cywKIyAgICAgICAgICAgICAgZGF0YSA9IGR0X2ZpbmFsKQojIHN1bW1hcnkoZml0KQoKZml0IDwtIGNveHBoKFN1cnYodHN0YXJ0LCB0c3RvcCwgb3NfZXZlbnQpIH4gYmlvbWFya2VyX3N0YXR1cywKICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKc3VtbWFyeShmaXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCmBgYAoKCiNQRlMgYnkgY3RETkEgc3RhdHVzIGF0IHN1cnZlaWxsYW5jZSBTdGFnZSBJL0lJCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmF2YWlsYWJsZT09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY1N0YWdlPT0iSS9JSSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShQRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gVFJVRSwgY29uZi5pbnQgPSBGQUxTRSwgcmlzay50YWJsZSA9IFRSVUUsIGJyZWFrLnRpbWUuYnk9NiwgcGFsZXR0ZT1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJQRlMgLSBjdEROQSBhdCBTdXJ2ZWlsbGFuY2UgU3RhZ2UgSS9JSSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoZihzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YT1jaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSBTdGFnZSBJL0lJIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNQRlMgYnkgY3RETkEgc3RhdHVzIGF0IHN1cnZlaWxsYW5jZSBTdGFnZSBJSUkvSVYKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjU3RhZ2U9PSJJSUkvSVYiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KX5jdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLlN1cnZlaWxsYW5jZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oUEZTLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT02LCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlBGUyAtIGN0RE5BIGF0IFN1cnZlaWxsYW5jZSBTdGFnZSBJSUkvSVYiLCB5bGFiPSAiUHJvZ3Jlc3Npb24tRnJlZSBTdXJ2aXZhbCIsIHhsYWI9Ik1vbnRocyBmcm9tIERlZmluaXRpdmUgVHJlYXRtZW50IiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMjQsIDM2KSkKY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY294X2ZpdCA8LSBjb3hwaChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LGRhdGEgPSBjaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQoKY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzID0gYygiTkVHQVRJVkUiLCAiUE9TSVRJVkUiKSwgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJFBGUy5FdmVudCA8LSBmYWN0b3IoY2lyY19kYXRhJFBGUy5FdmVudCwgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpLCBsYWJlbHMgPSBjKCJObyBQcm9ncmVzc2lvbiIsICJQcm9ncmVzc2lvbiIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBjaXJjX2RhdGEkUEZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBTdXJ2ZWlsbGFuY2UgU3RhZ2UgSUlJL0lWIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNQRlMgYnkgY3RETkEgc3RhdHVzIGF0IHN1cnZlaWxsYW5jZSBwMTYoKykKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRwMTYuc3RhdHVzPT0iUG9zaXRpdmUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIT0iIixdCmNpcmNfZGF0YWRmIDwtIGFzLmRhdGEuZnJhbWUoY2lyY19kYXRhKQoKc3VydmZpdChTdXJ2KHRpbWUgPSBjaXJjX2RhdGEkUEZTLm1vbnRocywgZXZlbnQgPSBjaXJjX2RhdGEkUEZTLkV2ZW50KX5jdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEpCmV2ZW50X3N1bW1hcnkgPC0gY2lyY19kYXRhICU+JQogIGdyb3VwX2J5KGN0RE5BLlN1cnZlaWxsYW5jZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgVG90YWwgPSBuKCksCiAgICBFdmVudHMgPSBzdW0oUEZTLkV2ZW50KSwKICAgIEZyYWN0aW9uID0gRXZlbnRzIC8gbigpLAogICAgUGVyY2VudGFnZSA9IChFdmVudHMgLyBuKCkpICogMTAwCiAgKQpwcmludChldmVudF9zdW1tYXJ5KQpzdXJ2X29iamVjdCA8LVN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IFRSVUUsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUEZTIC0gY3RETkEgYXQgU3VydmVpbGxhbmNlIHAxNigrKSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoZihzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YT1jaXJjX2RhdGEpIApzdW1tYXJ5KGNveF9maXQpCmNveF9maXRfc3VtbWFyeSA8LSBzdW1tYXJ5KGNveF9maXQpCgpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSBwMTYoKykiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlByb2dyZXNzaW9uIiwKICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiRmlzaGVyJ3MgZXhhY3QgdGVzdCBwLXZhbHVlOiAiLCBmb3JtYXQucHZhbChmaXNoZXJfZXhhY3RfdGVzdCRwLnZhbHVlKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gUHJvZ3Jlc3Npb24iID0gImJsdWUiLCAiUHJvZ3Jlc3Npb24iID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBQcm9ncmVzc2lvbiBsYWJlbCBzaXplCmBgYAoKI1BGUyBieSBjdEROQSBzdGF0dXMgYXQgc3VydmVpbGxhbmNlIHAxNigtKQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJDTElBIEhOU0NDIFBlZGRhZGEgQ2xpbmljYWwgRGF0YS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5hdmFpbGFibGU9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJHAxNi5zdGF0dXM9PSJOZWdhdGl2ZSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShQRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUEZTIC0gY3RETkEgYXQgU3VydmVpbGxhbmNlIHAxNigtKSIsIHlsYWI9ICJQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iTW9udGhzIGZyb20gRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCwgMzYpKQpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuU3VydmVpbGxhbmNlLCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlLCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUEZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFByb2dyZXNzaW9uIiwgIlByb2dyZXNzaW9uIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRQRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSBwMTYoLSkiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlByb2dyZXNzaW9uIiwKICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiRmlzaGVyJ3MgZXhhY3QgdGVzdCBwLXZhbHVlOiAiLCBmb3JtYXQucHZhbChmaXNoZXJfZXhhY3RfdGVzdCRwLnZhbHVlKSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTm8gUHJvZ3Jlc3Npb24iID0gImJsdWUiLCAiUHJvZ3Jlc3Npb24iID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBQcm9ncmVzc2lvbiBsYWJlbCBzaXplCmBgYAoKI011bHRpdmFyaWF0ZSBjb3ggcmVncmVzc2lvbiBmb3IgUEZTIGN0RE5BIHN0YXR1cyBhdCBzdXJ2ZWlsbGFuY2UKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBQZWRkYWRhIENsaW5pY2FsIERhdGEuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYXZhaWxhYmxlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRjU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjU3RhZ2UsIGxldmVscyA9IGMoIkkvSUkiLCAiSUlJL0lWIikpCmNpcmNfZGF0YSRwMTYuc3RhdHVzIDwtIGZhY3RvcihjaXJjX2RhdGEkcDE2LnN0YXR1cywgbGV2ZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSkKY2lyY19kYXRhJFByaW0uTG9jYXRpb24gPC0gZmFjdG9yKGNpcmNfZGF0YSRQcmltLkxvY2F0aW9uLCBsZXZlbHMgPSBjKCJPcm9waGFyeW54IiwgIkxhcnlueC9IeXBvcGhhcnlueCIsICJPcmFsIGNhdml0eSIsICJPdGhlciAocGFyYW5hc2FsIHNpbnVzIGFuZCBuYXNvcGhhcnluZ2VhbCkiKSkKc3Vydl9vYmplY3QgPC0gU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkgCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UgKyBjU3RhZ2UgKyBwMTYuc3RhdHVzICsgUHJpbS5Mb2NhdGlvbiwgZGF0YT1jaXJjX2RhdGEpIApnZ2ZvcmVzdChjb3hfZml0LCBkYXRhID0gY2lyY19kYXRhLCBtYWluID0gIk11bHRpdmFyaWF0ZSBSZWdyZXNzaW9uIE1vZGVsIGZvciBQRlMiLCByZWZMYWJlbCA9ICJSZWZlcmVuY2UgR3JvdXAiKQp0ZXN0LnBoIDwtIGNveC56cGgoY294X2ZpdCkKYGBgCgoKI2N0RE5BIGFuZCBNVE0vbUwgRHluYW1pY3MgZm9yIHB0cyBhdCBzdXJ2ZWlsbGFuY2Ugd2luZG93CmBgYHtyfQojRHluYW1pY3MgYW5kIE1UTS9tTCBwbG90cyBmb3IgcGF0aWVudHMgd2l0aCBjdEROQSBuZWdhdGl2ZSBhdCBzdXJ2ZWlsbGFuY2UKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpkZiA8LSByZWFkLmNzdigiQ0xJQSBITlNDQyBjdEROQSBNVE0uY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpkZiA8LSBkZltkZiRjdEROQS5TdXJ2ZWlsbGFuY2U9PSJORUdBVElWRSIsXQoKZGYkUEZTLkV2ZW50IDwtIGlmZWxzZShkZiRQRlMuRXZlbnQgJWluJSBjKCJObyIsICJubyIsICJGQUxTRSIsICJGYWxzZSIsICIwIiksIEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkZiRQRlMuRXZlbnQgJWluJSBjKCJZZXMiLCAieWVzIiwgIlRSVUUiLCAiVHJ1ZSIsICIxIiksIFRSVUUsIE5BKSkKZGYkUEZTLkV2ZW50IDwtIGZhY3RvcihkZiRQRlMuRXZlbnQsIGxldmVscyA9IGMoRkFMU0UsIFRSVUUpKQpkZiA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZmlsdGVyKG4oKSA+PSAyKSAlPiUgI2tlZXAgb25seSBwdHMgd2l0aCBhdCBsZWFzdCAyIHBvc3Qtc3VyZ2VyeSB0aW1lIHBvaW50cwogIHVuZ3JvdXAoKQoKbnVtX3VuaXF1ZSA8LSBsZW5ndGgodW5pcXVlKGRmJFBhdGllbnROYW1lKSkKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzOiIsIG51bV91bmlxdWUsICJcbiIpCgpkZl9wYXRpZW50X3BmcyA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSgKICAgIFBGU19UcnVlID0gYW55KFBGUy5FdmVudCA9PSBUUlVFLCBuYS5ybSA9IFRSVUUpLAogICAgUEZTX0ZhbHNlID0gYWxsKFBGUy5FdmVudCA9PSBGQUxTRSwgbmEucm0gPSBUUlVFKQogICkKCm51bV90cnVlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfVHJ1ZSkKbnVtX2ZhbHNlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfRmFsc2UpCgpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHMgd2l0aCBFdmVudDoiLCBudW1fdHJ1ZSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzIHdpdGggTm8gRXZlbnQ6IiwgbnVtX2ZhbHNlLCAiXG4iKQoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgCiAgICAgICAgICAgICAgICAgICAgeSA9IE1UTS5tTCwgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYXRpZW50TmFtZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBQRlMuRXZlbnQpKSArCiAgZ2VvbV9saW5lKCkgKyAgICAgICMgQ29ubmVjdCB0aW1lcG9pbnRzIGZvciBlYWNoIHBhdGllbnQKICBnZW9tX3BvaW50KCkgKyAgICAgIyBBZGQgcG9pbnRzIGZvciBlYWNoIHRpbWVwb2ludAogICMgVXNlIGEgbG9nMTAgc2NhbGUgZm9yIHRoZSB5LWF4aXMgd2l0aCBzcGVjaWZpZWQgYnJlYWtzCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSBjKDAuMDEsIDAuMSwgMSwgMTAsIDEwMCwgMTAwMCksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwLjAxIiwiMC4xIiwgIjEiLCAiMTAiLCAiMTAwIiwgIjEwMDAiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KGRmJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gNikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gImJsdWUiLCAiVFJVRSIgPSAicmVkIikpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIFNpbmNlIFN1cmdlcnkgb3Igc3RhcnQgb2YgZGVmaW5pdGl2ZSB0cmVhdG1lbnQgKG1vbnRocykiLAogICAgeSA9ICJNZWFuIFR1bW9yIE1vbGVjdWxlcyBwZXIgbUwgKE1UTS9tTCkiLAogICAgY29sb3IgPSAiUEZTIEV2ZW50IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpwcmludChwKQoKI0R5bmFtaWNzIGFuZCBNVE0vbUwgcGxvdHMgZm9yIHBhdGllbnRzIHdpdGggY3RETkEgcG9zaXRpdmUgYXQgc3VydmVpbGxhbmNlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgY3RETkEgTVRNLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZGYgPC0gZGZbZGYkY3RETkEuU3VydmVpbGxhbmNlPT0iUE9TSVRJVkUiLF0KCmRmJFBGUy5FdmVudCA8LSBpZmVsc2UoZGYkUEZTLkV2ZW50ICVpbiUgYygiTm8iLCAibm8iLCAiRkFMU0UiLCAiRmFsc2UiLCAiMCIpLCBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGYkUEZTLkV2ZW50ICVpbiUgYygiWWVzIiwgInllcyIsICJUUlVFIiwgIlRydWUiLCAiMSIpLCBUUlVFLCBOQSkpCmRmJFBGUy5FdmVudCA8LSBmYWN0b3IoZGYkUEZTLkV2ZW50LCBsZXZlbHMgPSBjKEZBTFNFLCBUUlVFKSkKZGYgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogIGZpbHRlcihuKCkgPj0gMikgJT4lICNrZWVwIG9ubHkgcHRzIHdpdGggYXQgbGVhc3QgMiBwb3N0LXN1cmdlcnkgdGltZSBwb2ludHMKICB1bmdyb3VwKCkKCm51bV91bmlxdWUgPC0gbGVuZ3RoKHVuaXF1ZShkZiRQYXRpZW50TmFtZSkpCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBwYXRpZW50czoiLCBudW1fdW5pcXVlLCAiXG4iKQoKZGZfcGF0aWVudF9wZnMgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBQRlNfVHJ1ZSA9IGFueShQRlMuRXZlbnQgPT0gVFJVRSwgbmEucm0gPSBUUlVFKSwKICAgIFBGU19GYWxzZSA9IGFsbChQRlMuRXZlbnQgPT0gRkFMU0UsIG5hLnJtID0gVFJVRSkKICApCgpudW1fdHJ1ZSA8LSBzdW0oZGZfcGF0aWVudF9wZnMkUEZTX1RydWUpCm51bV9mYWxzZSA8LSBzdW0oZGZfcGF0aWVudF9wZnMkUEZTX0ZhbHNlKQoKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzIHdpdGggRXZlbnQ6IiwgbnVtX3RydWUsICJcbiIpCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBwYXRpZW50cyB3aXRoIE5vIEV2ZW50OiIsIG51bV9mYWxzZSwgIlxuIikKCnAgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIAogICAgICAgICAgICAgICAgICAgIHkgPSBNVE0ubUwsIAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gUGF0aWVudE5hbWUsIAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gUEZTLkV2ZW50KSkgKwogIGdlb21fbGluZSgpICsgICAgICAjIENvbm5lY3QgdGltZXBvaW50cyBmb3IgZWFjaCBwYXRpZW50CiAgZ2VvbV9wb2ludCgpICsgICAgICMgQWRkIHBvaW50cyBmb3IgZWFjaCB0aW1lcG9pbnQKICAjIFVzZSBhIGxvZzEwIHNjYWxlIGZvciB0aGUgeS1heGlzIHdpdGggc3BlY2lmaWVkIGJyZWFrcwogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gYygwLjAxLCAwLjEsIDEsIDEwLCAxMDAsIDEwMDAsIDEwMDAwKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAuMDEiLCIwLjEiLCAiMSIsICIxMCIsICIxMDAiLCAiMTAwMCIsICIxMDAwMCIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgoZGYkZGF0ZS5kaWZmLm1vbnRocywgbmEucm0gPSBUUlVFKSwgYnkgPSA2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJGQUxTRSIgPSAiYmx1ZSIsICJUUlVFIiA9ICJyZWQiKSkgKwogIGxhYnMoCiAgICB4ID0gIlRpbWUgU2luY2UgU3VyZ2VyeSBvciBzdGFydCBvZiBkZWZpbml0aXZlIHRyZWF0bWVudCAobW9udGhzKSIsCiAgICB5ID0gIk1lYW4gVHVtb3IgTW9sZWN1bGVzIHBlciBtTCAoTVRNL21MKSIsCiAgICBjb2xvciA9ICJQRlMgRXZlbnQiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCnByaW50KHApCmBgYAoKI1BGUyBieSBjdEROQSBzdGF0dXMgYXQgc3VydmVpbGxhbmNlIGZvciBwdHMgd2l0aCBNUkQgJiBTdXJ2ZWlsbGFuY2UgdGltZSBwb2ludHMgYXZhaWxhYmxlCmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmNvbXBsZXRlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRQRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRQRlMuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShQRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFBGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFBGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUEZTIC0gY3RETkEgYXQgU3VydmVpbGxhbmNlIiwgeWxhYj0gIlByb2dyZXNzaW9uLUZyZWUgU3Vydml2YWwiLCB4bGFiPSJNb250aHMgZnJvbSBEZWZpbml0aXZlIFRyZWF0bWVudCIsIGxlZ2VuZC5sYWJzPWMoImN0RE5BIE5lZ2F0aXZlIiwgImN0RE5BIFBvc2l0aXZlIiksIGxlZ2VuZC50aXRsZT0iIikKc3VtbWFyeShLTV9jdXJ2ZSwgdGltZXM9IGMoMCwgMTIsIDI0LCAzNikpCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCxkYXRhID0gY2lyY19kYXRhKSAKc3VtbWFyeShjb3hfZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShjb3hfZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscyA9IGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRQRlMuRXZlbnQgPC0gZmFjdG9yKGNpcmNfZGF0YSRQRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUHJvZ3Jlc3Npb24iLCAiUHJvZ3Jlc3Npb24iKSkKY29udGluZ2VuY3lfdGFibGUgPC0gdGFibGUoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgY2lyY19kYXRhJFBGUy5FdmVudCkKY2hpX3NxdWFyZV90ZXN0IDwtIGNoaXNxLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGNoaV9zcXVhcmVfdGVzdCkKZmlzaGVyX2V4YWN0X3Rlc3QgPC0gZmlzaGVyLnRlc3QoY29udGluZ2VuY3lfdGFibGUpCnByaW50KGZpc2hlcl9leGFjdF90ZXN0KQpwcmludChjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYgPC0gYXMuZGF0YS5mcmFtZShjb250aW5nZW5jeV90YWJsZSkKdGFibGVfZGYkVG90YWwgPC0gYXZlKHRhYmxlX2RmJEZyZXEsIHRhYmxlX2RmJFZhcjEsIEZVTiA9IHN1bSkKdGFibGVfZGYkUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRGcmVxIC8gdGFibGVfZGYkVG90YWwKdGFibGVfZGYkTWlkZGxlUGVyY2VudGFnZSA8LSB0YWJsZV9kZiRQZXJjZW50YWdlIC8gMgpnZ3Bsb3QodGFibGVfZGYsIGFlcyh4ID0gVmFyMSwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBWYXIyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gTWlkZGxlUGVyY2VudGFnZSwgbGFiZWwgPSBGcmVxKSwgcG9zaXRpb24gPSAic3RhY2siLCBjb2xvciA9ICJibGFjayIsIHZqdXN0ID0gMS41LCBzaXplID0gNykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJjdEROQSBzdGF0dXMgYXQgU3VydmVpbGxhbmNlIiwgCiAgICAgICB4ID0gImN0RE5BIiwgCiAgICAgICB5ID0gIlBhdGllbnRzICglKSIsIAogICAgICAgZmlsbCA9ICJQcm9ncmVzc2lvbiIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFByb2dyZXNzaW9uIiA9ICJibHVlIiwgIlByb2dyZXNzaW9uIiA9ICJyZWQiKSkgKyAjIGRlZmluZSBjdXN0b20gY29sb3JzCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEuNSwgc2l6ZSA9IDE0KSwgIyBpbmNyZWFzZSB4LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHgtYXhpcyBsYWJlbCBzaXplCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSkgICMgaW5jcmVhc2UgUHJvZ3Jlc3Npb24gbGFiZWwgc2l6ZQpgYGAKCiNUaW1lLWRlcGVuZGVudCBhbmFseXNpcyBmb3IgUEZTIGluIGxvbmdpdHVkaW5hbCB0aW1lIHBvaW50cyBmb3IgcHRzIHdpdGggTVJEICYgU3VydmVpbGxhbmNlIHRpbWUgcG9pbnRzIGF2YWlsYWJsZQpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpkdCA8LSByZWFkX3hsc3goIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhX1RpbWUgZGVwZW5kZW50Lnhsc3giKSB8PgogIGNsZWFuX25hbWVzKCkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gYyh3aW5kb3dfc3RhcnRfZGF0ZSxkZnNfZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKGFzLkRhdGUoLngsIGZvcm1hdCA9ICIlWS0lbS0lZCIpKSkpCgpkdF9iaW9tYXJrZXIgPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfY29tcGxldGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9zdGF0dXM6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgZmlsdGVyKGN0X2RuYV9jb21wbGV0ZSkgfD4KICBwaXZvdF9sb25nZXIoY29scyA9IHN1cnZlaWxsYW5jZV8xX3N0YXR1czpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJ2aXNpdF9udW1iZXIiLCAiLnZhbHVlIiksCiAgICAgICAgICAgICAgIG5hbWVzX3BhdHRlcm4gPSAic3VydmVpbGxhbmNlXyguKV8oLiopIikgfD4KICBtdXRhdGUoYmlvbWFya2VyX3RpbWUgPSBkYXkoZGF5cyhkYXRlIC0gd2luZG93X3N0YXJ0X2RhdGUpKSkgfD4KICBzZWxlY3QocHRzX2lkLCBiaW9tYXJrZXJfdGltZSwgYmlvbWFya2VyX3N0YXR1cyA9IHN0YXR1cykgfD4KICBmaWx0ZXIoIWlzLm5hKGJpb21hcmtlcl90aW1lKSkKCmdsaW1wc2UoZHRfYmlvbWFya2VyKQoKZHRfc3Vydml2YWwgPC0gZHQgfD4KICBzZWxlY3QocHRzX2lkLCBjdF9kbmFfY29tcGxldGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlOmRmc19kYXRlLCBkZnNfZXZlbnQpIHw+ICAjIEFkZGVkIGRmc19ldmVudCBoZXJlCiAgZmlsdGVyKGN0X2RuYV9jb21wbGV0ZSkgfD4KICBtdXRhdGUoZGZzX3RpbWUgPSAoZGZzX2RhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSksCiAgICAgICAgIGRmc190aW1lID0gZGF5KGRheXMoZGZzX3RpbWUpKSwKICAgICAgICAgZGZzX2V2ZW50ID0gYXMubnVtZXJpYyhkZnNfZXZlbnQpKSB8PgogIHNlbGVjdChwdHNfaWQsIGRmc190aW1lLCBkZnNfZXZlbnQpCgpnbGltcHNlKGR0X3N1cnZpdmFsKQoKYXV4IDwtIGR0X3N1cnZpdmFsICU+JSAKICBmaWx0ZXIoZGZzX3RpbWUgPD0gMCkKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBzZWxlY3QocHRzX2lkLCB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX3RpbWUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLCBkZnNfdGltZSkKCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKZHRfc3Vydml2YWwgPC0gZHRfc3Vydml2YWwgfD4KICBmaWx0ZXIoZGZzX3RpbWUgPiAwKQoKYXV4IDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX2NvbXBsZXRlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggLSB3aW5kb3dfc3RhcnRfZGF0ZSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggPCAwKSkgfD4KICByb3d3aXNlKCkgfD4KICBtdXRhdGUoc3VtX25lZyA9IAogICAgICAgICAgIHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSwKICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZykKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBmaWx0ZXIoc3VtX25lZyA+IDApIHw+CiAgc2VsZWN0KHB0c19pZCwgc3VtX25lZywgd2luZG93X3N0YXJ0X2RhdGUsCiAgICAgICAgIHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHdpbmRvd19zdGFydF9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlLCAKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGFzX2RhdGUoLngpKSkgCgpkYXRhdGFibGUodGFiLCBmaWx0ZXIgPSAidG9wIikKCmF1eCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9jb21wbGV0ZSwKICAgICAgICAgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCAtIHdpbmRvd19zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwKICAgICAgICAgICAgICAgIC5mbnMgPSB+IGRmc19kYXRlIDwgLngpKSB8PgogIHJvd3dpc2UoKSB8PgogIG11dGF0ZShuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA9IHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMl9kYXRlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xNF9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzE0X2RhdGUsCiAgICAgICAgICAgICAgICAuZm5zID0gfiAhaXMubmEoLngpKSkgfD4KICBtdXRhdGUodG90YWxfYmlvbWFya2VyID0gc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8yX2RhdGU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZlaWxsYW5jZV8xNF9kYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpCgp0ZW1wIDwtIGF1eCB8PiAKICBzZWxlY3QoLXB0c19pZCkgfD4gCiAgZ3JvdXBfYnkobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlcikgfD4gICMgRGlyZWN0IGdyb3VwaW5nCiAgc3VtbWFyaXNlKGZyZXEgPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpICAjIERyb3AgZ3JvdXBzIGFmdGVyIHN1bW1hcml6YXRpb24KCgp0YWIgPC0gbGVmdF9qb2luKGF1eCwgZHQpIHw+CiAgc2VsZWN0KHB0c19pZCwgbl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQsIHRvdGFsX2Jpb21hcmtlciwgCiAgICAgICAgIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMl9kYXRlOnN1cnZlaWxsYW5jZV8xNF9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTRfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgZmlsdGVyKG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50ID4gMCkKZGF0YXRhYmxlKHRhYiwgZmlsdGVyID0gInRvcCIpCgphdXggPC0gdG1lcmdlKGRhdGExID0gZHRfc3Vydml2YWwsIAogICAgICAgICAgICAgIGRhdGEyID0gZHRfc3Vydml2YWwsCiAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgIGRmc19ldmVudCA9IGV2ZW50KGRmc190aW1lLCBkZnNfZXZlbnQpKQpkdF9maW5hbCA8LSB0bWVyZ2UoZGF0YTEgPSBhdXgsIAogICAgICAgICAgICAgICAgICAgZGF0YTIgPSBkdF9iaW9tYXJrZXIsCiAgICAgICAgICAgICAgICAgICBpZCA9IHB0c19pZCwgCiAgICAgICAgICAgICAgICAgICBiaW9tYXJrZXJfc3RhdHVzID0gCiAgICAgICAgICAgICAgICAgICAgIHRkYyhiaW9tYXJrZXJfdGltZSwgYmlvbWFya2VyX3N0YXR1cykpCgpkYXRhdGFibGUoZHRfZmluYWwsIGZpbHRlciA9ICJ0b3AiKQoKIyBTeW50YXggaWYgdGhlcmUgaXMgbm90IHRpbWUtZGVwZW5kZW50IGNvdmFyaWF0ZQojIGZpdCA8LSBjb3hwaChTdXJ2KGRmc190aW1lLCBkZnNfZXZlbnQpIH4gYmlvbWFya2VyX3N0YXR1cywKIyAgICAgICAgICAgICAgZGF0YSA9IGR0X2ZpbmFsKQojIHN1bW1hcnkoZml0KQoKZml0IDwtIGNveHBoKFN1cnYodHN0YXJ0LCB0c3RvcCwgZGZzX2V2ZW50KSB+IGJpb21hcmtlcl9zdGF0dXMsCiAgICAgICAgICAgICBkYXRhID0gZHRfZmluYWwpCnN1bW1hcnkoZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShmaXQpCgojRXh0cmFjdCB2YWx1ZXMgZm9yIEhSLCA5NSUgQ0ksIGFuZCBwLXZhbHVlCkhSIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbMl0KbG93ZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzNdCnVwcGVyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFs0XQpwX3ZhbHVlIDwtIGNveF9maXRfc3VtbWFyeSRjb2VmZmljaWVudHNbNV0KbGFiZWxfdGV4dCA8LSBwYXN0ZTAoIkhSID0gIiwgcm91bmQoSFIsIDIpLCAiICgiLCByb3VuZChsb3dlcl9DSSwgMiksICItIiwgcm91bmQodXBwZXJfQ0ksIDIpLCAiKTsgcCA9ICIsIHJvdW5kKHBfdmFsdWUsIDMpKQpwcmludChsYWJlbF90ZXh0KQpgYGAKCgojTWVkaWFuIG51bWJlcnMgb2YgdGltZSBwb2ludHMgYW5kIGxlYWQgdGltZSBpbiB0aGUgbG9uZ2l0dWRpbmFsIHNldHRpbmcgZm9yIHB0cyB3aXRoIE1SRCAmIFN1cnZlaWxsYW5jZSB0aW1lIHBvaW50cyBhdmFpbGFibGUKYGBge3J9CiMgTG9hZCB0aGUgZGF0YXNldApybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgUGVkZGFkYSBDbGluaWNhbCBEYXRhLmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLmNvbXBsZXRlPT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgptZWRpYW5fTnN1cnZ0cHMgPC0gbWVkaWFuKGNpcmNfZGF0YWRmJE5zdXJ2dHBzLCBuYS5ybSA9IFRSVUUpCm1pbl9Oc3VydnRwcyA8LSBtaW4oY2lyY19kYXRhZGYkTnN1cnZ0cHMsIG5hLnJtID0gVFJVRSkKbWF4X05zdXJ2dHBzIDwtIG1heChjaXJjX2RhdGFkZiROc3VydnRwcywgbmEucm0gPSBUUlVFKQoKY2F0KHNwcmludGYoIk1lZGlhbiAjIG9mIHN1cnZlaWxsYW5jZSB0aW1lIHBvaW50czogJWQgKCVkLSVkKVxuIiwgCiAgICAgICAgICAgIG1lZGlhbl9Oc3VydnRwcywgbWluX05zdXJ2dHBzLCBtYXhfTnN1cnZ0cHMpKQpgYGAKCiNNZWRpYW4gb2YgbWVkaWFuIGludGVydmFsIG9mIGN0RE5BIHRpbWVwb2ludHMgYW5kIHJhZGlvbG9naWNhbCBpbWFnaW5nIGFzc2Vzc21lbnQKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIkNMSUEgSE5TQ0MgVU5NX09QLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCm5hbWVzKGRmKSA8LSB0cmltd3MobmFtZXMoZGYpKQoKIyAtLS0tIEhlbHBlcjogY29tcHV0ZSBtZWRpYW4gaW50ZXJ2YWwgcGVyIHBhdGllbnQgLS0tLQptZWRpYW5faW50ZXJ2YWxfcGVyX3BhdGllbnQgPC0gZnVuY3Rpb24oZGF0YSwgZmlsdGVyX2NvbCwgZmlsdGVyX3ZhbCkgewogIGRhdGEgJT4lCiAgICBmaWx0ZXIoISFzeW0oZmlsdGVyX2NvbCkgPT0gZmlsdGVyX3ZhbCkgJT4lCiAgICBhcnJhbmdlKFBhdGllbnROYW1lLCBkYXRlLmRpZmYpICU+JQogICAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogICAgbXV0YXRlKGludGVydmFsID0gZGF0ZS5kaWZmIC0gbGFnKGRhdGUuZGlmZikpICU+JQogICAgc3VtbWFyaXNlKG1lZGlhbl9pbnRlcnZhbCA9IG1lZGlhbihpbnRlcnZhbCwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgICBtdXRhdGUoZXZlbnRfZ3JvdXAgPSBmaWx0ZXJfdmFsKQp9CgojIC0tLS0gQ29tcHV0ZSBwZXItcGF0aWVudCBtZWRpYW5zIC0tLS0KY3RETkFfcGF0aWVudF9tZWRpYW5zICAgPC0gbWVkaWFuX2ludGVydmFsX3Blcl9wYXRpZW50KGRmLCAiRXZlbnQiLCAiY3RETkEiKQppbWFnaW5nX3BhdGllbnRfbWVkaWFucyA8LSBtZWRpYW5faW50ZXJ2YWxfcGVyX3BhdGllbnQoZGYsICJFdmVudF90eXBlIiwgIkltYWdpbmciKQoKIyAtLS0tIEZ1bmN0aW9uIHRvIHN1bW1hcml6ZSBwYXRpZW50LWxldmVsIG1lZGlhbnMgdG8gY29ob3J0LWxldmVsIC0tLS0KY29ob3J0X3N1bW1hcnkgPC0gZnVuY3Rpb24ocGF0aWVudF9kYXRhLCBldmVudF9sYWJlbCkgewogICMgRmlsdGVyIG91dCBwYXRpZW50cyB3aXRoIE5BIG1lZGlhbiBpbnRlcnZhbHMKICB2YWxpZF9kYXRhIDwtIHBhdGllbnRfZGF0YSAlPiUgZmlsdGVyKCFpcy5uYShtZWRpYW5faW50ZXJ2YWwpKQogIAogIGRhdGEuZnJhbWUoCiAgICBFdmVudF9UeXBlID0gZXZlbnRfbGFiZWwsCiAgICBDb2hvcnRfTWVkaWFuX0ZyZXF1ZW5jeSA9IG1lZGlhbih2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKSwKICAgIE1pbl9QYXRpZW50X01lZGlhbiA9IG1pbih2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKSwKICAgIE1heF9QYXRpZW50X01lZGlhbiA9IG1heCh2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKSwKICAgIFJhbmdlX1BhdGllbnRfTWVkaWFuID0gbWF4KHZhbGlkX2RhdGEkbWVkaWFuX2ludGVydmFsLCBuYS5ybSA9IFRSVUUpIC0gCiAgICAgIG1pbih2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKQogICkKfQoKIyAtLS0tIENvbWJpbmUgYWxsIHJlc3VsdHMgLS0tLQpyZXN1bHRzIDwtIGJpbmRfcm93cygKICBjb2hvcnRfc3VtbWFyeShjdEROQV9wYXRpZW50X21lZGlhbnMsICJjdEROQSIpLAogIGNvaG9ydF9zdW1tYXJ5KGltYWdpbmdfcGF0aWVudF9tZWRpYW5zLCAiSW1hZ2luZyIpCikKCiMgLS0tLSBQcmludCBjb2hvcnQtbGV2ZWwgc3VtbWFyeSAtLS0tCmNhdCgiPT09PT0gTWVkaWFuIEZyZXF1ZW5jeSAoRGF5cykgcGVyIENvaG9ydCA9PT09PVxuIikKcHJpbnQocmVzdWx0cywgcm93Lm5hbWVzID0gRkFMU0UpCgojIC0tLS0gT1BUSU9OQUw6IFNhdmUgcGF0aWVudC1sZXZlbCBtZWRpYW5zIC0tLS0KcGF0aWVudF9sZXZlbF9tZWRpYW5zIDwtIGJpbmRfcm93cyhjdEROQV9wYXRpZW50X21lZGlhbnMsIGltYWdpbmdfcGF0aWVudF9tZWRpYW5zKQojd3JpdGUuY3N2KHBhdGllbnRfbGV2ZWxfbWVkaWFucywgInBhdGllbnRfbWVkaWFuX2ludGVydmFscy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojUGxvdCBmb3IgaW5kaXZpZHVhbCBsZWFkLXRpbWUgY2FsY3VsYXRpb25zIGZvciBlYWNoIHB0CmBgYHtyfQpybShsaXN0PWxzKCkpCmNzdl9wYXRoIDwtICJ+L0Rvd25sb2Fkcy9DTElBIEhOU0NDIFBlZGRhZGFfTGVhZCBUaW1lIHB0cy5jc3YiCmRmIDwtIHJlYWQuY3N2KGNzdl9wYXRoLCBjaGVjay5uYW1lcyA9IEZBTFNFKQppZF9jYW5kaWRhdGVzIDwtIGMoIlBhdGllbnQiLCJJRCIsIlB0IiwiU3ViamVjdCIsIlNhbXBsZSIpCmlkX2NvbCA8LSBpZF9jYW5kaWRhdGVzW2lkX2NhbmRpZGF0ZXMgJWluJSBuYW1lcyhkZildWzFdCmlmIChpcy5uYShpZF9jb2wpKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShQYXRpZW50ID0gcm93X251bWJlcigpKQogIGlkX2NvbCA8LSAiUGF0aWVudCIKfQoKbnVtX2NvbHMgPC0gaW50ZXJzZWN0KGMoIk1SIiwiQ1IiLCJMVCIpLCBuYW1lcyhkZikpCmRmW251bV9jb2xzXSA8LSBsYXBwbHkoZGZbbnVtX2NvbHNdLCBmdW5jdGlvbih4KSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoeCkpKQoKIyBJZiBMVCBtaXNzaW5nLCBjb21wdXRlIGluIGRheXMKaWYgKCEoIkxUIiAlaW4lIG5hbWVzKGRmKSkpIHsKICBkZiA8LSBkZiAlPiUgbXV0YXRlKExUID0gQ1IgLSBNUikKfQoKIyAtLS0tIGNvbnZlcnQgdG8gbW9udGhzIC0tLS0KIyBBcHByb3hpbWF0ZTogMzAuNDQgZGF5cyBwZXIgbW9udGggKGF2ZXJhZ2UpCmRheXNfdG9fbW9udGhzIDwtIGZ1bmN0aW9uKHgpIHggLyAzMC40MzcKCmRmIDwtIGRmICU+JQogIG11dGF0ZShNUl9tbyA9IGRheXNfdG9fbW9udGhzKE1SKSwKICAgICAgICAgQ1JfbW8gPSBkYXlzX3RvX21vbnRocyhDUiksCiAgICAgICAgIExUX21vID0gZGF5c190b19tb250aHMoTFQpKQoKIyAtLS0tIG9yZGVyaW5nIGZvciB5LWF4aXMgLS0tLQpkZiA8LSBkZiAlPiUKICBtdXRhdGUoRWFybGllc3QgPSBwbWluKE1SX21vLCBDUl9tbywgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShFYXJsaWVzdCkgJT4lCiAgbXV0YXRlKFBhdGllbnRfZiA9IGZhY3RvciguZGF0YVtbaWRfY29sXV0sIGxldmVscyA9IC5kYXRhW1tpZF9jb2xdXSkpCgojIC0tLS0gbG9uZyBmb3JtYXQgZm9yIHBvaW50cyAtLS0tCnBvaW50c19sb25nIDwtIGRmICU+JQogIHNlbGVjdChQYXRpZW50X2YsIE1SX21vLCBDUl9tbykgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoTVJfbW8sIENSX21vKSwgbmFtZXNfdG8gPSAiVHlwZSIsIHZhbHVlc190byA9ICJNb250aHMiKSAlPiUKICBtdXRhdGUoVHlwZSA9IGRwbHlyOjpyZWNvZGUoVHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1SX21vIiA9ICJNb2xlY3VsYXIgcmVjdXJyZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDUl9tbyIgPSAiQ2xpbmljYWwgcmVjdXJyZW5jZSIpKQoKIyAtLS0tIHNlZ21lbnRzIGJldHdlZW4gTVIgYW5kIENSIC0tLS0Kc2VnbWVudHNfZGYgPC0gZGYgJT4lCiAgdHJhbnNtdXRlKFBhdGllbnRfZiwKICAgICAgICAgICAgeDAgPSBNUl9tbywgeDEgPSBDUl9tbywKICAgICAgICAgICAgbHRfZmxhZyA9IGlmX2Vsc2UoTFQgPiAxMjAsICJndDEyMCIsICJsZTEyMCIpKQoKIyAtLS0tIGFubm90YXRpb24gLS0tLQptZWRfbHQgIDwtIG1lZGlhbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptaW5fbHQgIDwtIG1pbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptYXhfbHQgIDwtIG1heChkZiRMVF9tbywgbmEucm0gPSBUUlVFKQoKYW5ub3RfbGFiZWwgPC0gc3ByaW50ZigiTWVkaWFuIGxlYWQtdGltZTpcbiUuMWYgbW9udGhzICglLjFmIHRvICUuMWYpIiwKICAgICAgICAgICAgICAgICAgICAgICBtZWRfbHQsIG1pbl9sdCwgbWF4X2x0KQoKIyAtLS0tIHBsb3QgLS0tLQpwYWwgPC0gYygiTW9sZWN1bGFyIHJlY3VycmVuY2UiID0gIiMxMEI0QzEiLAogICAgICAgICAiQ2xpbmljYWwgcmVjdXJyZW5jZSIgID0gIiNDOTZBNzIiKQoKeF9tYXggPC0gbWF4KGMoZGYkTVJfbW8sIGRmJENSX21vKSwgbmEucm0gPSBUUlVFKQp5X21pZCA8LSBsZXZlbHMoZGYkUGF0aWVudF9mKVtjZWlsaW5nKG5sZXZlbHMoZGYkUGF0aWVudF9mKSAqIDAuNTUpXQoKcCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBzZWdtZW50c19kZiwKICAgICAgICAgICAgICAgYWVzKHggPSB4MCwgeGVuZCA9IHgxLCB5ID0gUGF0aWVudF9mLCB5ZW5kID0gUGF0aWVudF9mLAogICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSBsdF9mbGFnKSwKICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC42LCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYyhsZTEyMCA9ICJzb2xpZCIsIGd0MTIwID0gImRhc2hlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICJub25lIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBvaW50c19sb25nLAogICAgICAgICAgICAgYWVzKHggPSBNb250aHMsIHkgPSBQYXRpZW50X2YsIGNvbG9yID0gVHlwZSksCiAgICAgICAgICAgICBzaXplID0gMi44KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbCwgbmFtZSA9IE5VTEwpICsKICBsYWJzKHggPSAiTW9udGhzIGZyb20gU3VyZ2VyeSBvciBEZWZpbml0aXZlIFRyZWF0bWVudCIsIHkgPSBOVUxMKSArCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSB4X21heCAqIDAuNzMsCiAgICAgICAgICAgeSA9IHlfbWlkLAogICAgICAgICAgIGxhYmVsID0gYW5ub3RfbGFiZWwsCiAgICAgICAgICAgaGp1c3QgPSAwLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMuOCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgY2VpbGluZyh4X21heC8zKSozLCBieSA9IDMpKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIHhfbWF4ICogMS4wNSkpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxMikgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5ODUiLCBsaW5ld2lkdGggPSAwLjMpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5OTIiLCBsaW5ld2lkdGggPSAwLjIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpCiAgKQoKcHJpbnQocCkKCndjX2RmIDwtIGRmICU+JSBkcGx5cjo6c2VsZWN0KE1SLCBDUikgJT4lIHRpZHlyOjpkcm9wX25hKCkKcGFpcmVkX2RpZmYgPC0gd2NfZGYkQ1IgLSB3Y19kZiRNUgoKd2lseCA8LSB3aWxjb3gudGVzdCgKICB4ID0gd2NfZGYkQ1IsIHkgPSB3Y19kZiRNUiwKICBwYWlyZWQgPSBUUlVFLAogIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsCiAgY29uZi5pbnQgPSBUUlVFLCAgICAgICMgZ2l2ZXMgQ0kgZm9yIHRoZSBIb2RnZXPigJNMZWhtYW5uIGVzdGltYXRlIG9mIHRoZSBtZWRpYW4gZGlmZmVyZW5jZQogIGV4YWN0ID0gRkFMU0UgICAgICAgICAjIHNhZmVyIGZvciB0aWVzL2xhcmdlIE4KKQoKV19zdGF0ICAgPC0gdW5uYW1lKHdpbHgkc3RhdGlzdGljKSAgICAgICAgICAjIFdpbGNveG9uIFYKcF92YWx1ZSAgPC0gd2lseCRwLnZhbHVlCkhMX2VzdCAgIDwtIHVubmFtZSh3aWx4JGVzdGltYXRlKSAgICAgICAgICAgIyBtZWRpYW4gb2YgKENSIC0gTVIpCkhMX2NpICAgIDwtIHdpbHgkY29uZi5pbnQgICAgICAgICAgICAgICAgICAgIyBDSSBmb3IgbWVkaWFuIGRpZmZlcmVuY2UKCiMgU2ltcGxlIHAtdmFsdWUgZm9ybWF0dGVyIGZvciBhbm5vdGF0aW9uL3B1YmxpY2F0aW9uCmZtdF9wIDwtIGZ1bmN0aW9uKHApIHsKICBpZiAoaXMubmEocCkpIHJldHVybigiTkEiKQogIGlmIChwIDwgMC4wMDEpICI8IDAuMDAxIiBlbHNlIHNwcmludGYoIj0gJS4zZiIsIHJvdW5kKHAsIDMpKQp9CnBfdGV4dCA8LSBwYXN0ZTAoIlAgIiwgZm10X3AocF92YWx1ZSkpICAjIGUuZy4sICJQID0gMC4wMDMiIG9yICJQIDwgMC4wMDEiCgojIE9wdGlvbmFsOiBwcmludCBhIGNvbXBhY3Qgc3VtbWFyeQpjYXQoIlxuUGFpcmVkIFdpbGNveG9uIHNpZ25lZC1yYW5rIHRlc3QgKENSIHZzIE1SKVxuIiwKICAgICItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iLAogICAgc3ByaW50ZigiTiBwYWlyczogJWQiLCBucm93KHdjX2RmKSksICJcbiIsCiAgICBzcHJpbnRmKCJXIChWKTogJWciLCBXX3N0YXQpLCAiXG4iLAogICAgc3ByaW50ZigiUC12YWx1ZTogJXMiLCBpZmVsc2UocF92YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHNwcmludGYoIiUuNmYiLCBwX3ZhbHVlKSkpLCAiXG4iLAogICAgc3ByaW50ZigiSG9kZ2Vz4oCTTGVobWFubiBtZWRpYW4gZGlmZmVyZW5jZSAoQ1IgLSBNUik6ICUuMWYgZGF5cyIsIEhMX2VzdCksICJcbiIsCiAgICBzcHJpbnRmKCI5NSUlIENJOiBbJS4xZiwgJS4xZl0gZGF5cyIsIEhMX2NpWzFdLCBITF9jaVsyXSksICJcbiIsIHNlcCA9ICIiKQpgYGAKCgojY3RETkEgdmVsb2NpdHkgYW5kIGxlYWQgdGltZSBsaW5lciByZWdyZXNzaW9uCmBgYHtyfQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBITlNDQyBQZWRkYWRhX2N0RE5BIHZlbG9jaXR5LmNzdiIgICMgPC0gYWRqdXN0IGlmIG5lZWRlZApkZl9yYXcgPC0gcmVhZC5jc3YoY3N2X3BhdGgsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmRmIDwtIGRmX3JhdyAlPiUKICByZW5hbWUoTVRNX21MID0gYE1UTS9tTGApICU+JQogIG11dGF0ZSgKICAgIGRheXNDUi5tb250aHMgPSBhcy5udW1lcmljKGRheXNDUi5tb250aHMpLAogICAgTVRNX21MID0gYXMubnVtZXJpYyhNVE1fbUwpCiAgKSAlPiUKICBmaWx0ZXIoIWlzLm5hKFBhdGllbnROYW1lKSwgIWlzLm5hKGRheXNDUi5tb250aHMpLCAhaXMubmEoTVRNX21MKSkKCnByZUNSIDwtIGRmICU+JQogIGZpbHRlcihkYXlzQ1IubW9udGhzIDw9IDAsIGlzLmZpbml0ZShNVE1fbUwpLCBNVE1fbUwgPiAwKQoKZWxpZ2libGUgPC0gcHJlQ1IgJT4lCiAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogIGZpbHRlcihuKCkgPj0gMiwgbl9kaXN0aW5jdChkYXlzQ1IubW9udGhzKSA+PSAyKSAlPiUKICB1bmdyb3VwKCkKaWYgKG5yb3coZWxpZ2libGUpID09IDApIHsKICB3YXJuaW5nKCJObyBwYXRpZW50cyBoYXZlIOKJpTIgdmFsaWQgcHJlLXJlY3VycmVuY2UgcG9pbnRzIHdpdGggZGlzdGluY3QgdGltZXM7IHJlZ3Jlc3Npb24gbGluZXMgd2lsbCBiZSBvbWl0dGVkLiIpCn0KCmZpdHMgPC0gZWxpZ2libGUgJT4lCiAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogIHN1bW1hcmlzZSh4X21pbiA9IG1pbihkYXlzQ1IubW9udGhzLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICBtdXRhdGUoZ3JpZCA9IG1hcCh4X21pbiwgfnNlcSgueCwgMCwgbGVuZ3RoLm91dCA9IDUwKSkpICU+JQogIHNlbGVjdChQYXRpZW50TmFtZSwgZ3JpZCkKCnByZWRpY3RfcGF0aWVudCA8LSBmdW5jdGlvbihkYXQsIG5ld3gpIHsKICBpZiAobGVuZ3RoKG5ld3gpID09IDApIHsKICAgIHJldHVybih0aWJibGUoZGF5c0NSLm1vbnRocyA9IG51bWVyaWMoMCksIE1UTV9tTCA9IG51bWVyaWMoMCkpKQogIH0KICBkYXQyIDwtIGRhdCAlPiUKICAgIGZpbHRlcihpcy5maW5pdGUoTVRNX21MKSwgTVRNX21MID4gMCkgJT4lCiAgICBtdXRhdGUobG9nX2N0ZG5hID0gbG9nMTAoTVRNX21MKSkKICBpZiAobnJvdyhkYXQyKSA8IDIgfHwgbl9kaXN0aW5jdChkYXQyJGRheXNDUi5tb250aHMpIDwgMiB8fCBhbnkoIWlzLmZpbml0ZShkYXQyJGxvZ19jdGRuYSkpKSB7CiAgICByZXR1cm4odGliYmxlKGRheXNDUi5tb250aHMgPSBudW1lcmljKDApLCBNVE1fbUwgPSBudW1lcmljKDApKSkKICB9CiAgbSA8LSBsbShsb2dfY3RkbmEgfiBkYXlzQ1IubW9udGhzLCBkYXRhID0gZGF0MikKICB0aWJibGUoCiAgICBkYXlzQ1IubW9udGhzID0gbmV3eCwKICAgIE1UTV9tTCA9IDEwIF4gcHJlZGljdChtLCBuZXdkYXRhID0gdGliYmxlKGRheXNDUi5tb250aHMgPSBuZXd4KSkKICApCn0KCnByZWRfbGluZXMgPC0gZWxpZ2libGUgJT4lCiAgZ3JvdXBfYnkoUGF0aWVudE5hbWUpICU+JQogIHRpZHlyOjpuZXN0KCkgJT4lICAgICAgICAgICAgICAgICAgICAgICAjIGxpc3QtY29sdW1uICJkYXRhIiBwZXIgcGF0aWVudAogIGxlZnRfam9pbihmaXRzLCBieSA9ICJQYXRpZW50TmFtZSIpICU+JSAjIGxpc3QtY29sdW1uICJncmlkIiBwZXIgcGF0aWVudAogIG11dGF0ZShwcmVkID0gbWFwMihkYXRhLCBncmlkLCB+cHJlZGljdF9wYXRpZW50KC54LCAueSkpKSAlPiUKICBzZWxlY3QoUGF0aWVudE5hbWUsIHByZWQpICU+JQogIHRpZHlyOjp1bm5lc3QocHJlZCkKCnBvb2xlZF9saW5lIDwtIHsKICBpZiAobnJvdyhwcmVDUikgPj0gMiAmJiBuX2Rpc3RpbmN0KHByZUNSJGRheXNDUi5tb250aHMpID49IDIpIHsKICAgIHBvb2xlZF94IDwtIHNlcShtaW4ocHJlQ1IkZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSwgMCwgbGVuZ3RoLm91dCA9IDEwMCkKICAgIHBvb2xlZF9maXQgPC0gbG0obG9nMTAoTVRNX21MKSB+IGRheXNDUi5tb250aHMsIGRhdGEgPSBwcmVDUikKICAgIHRpYmJsZSgKICAgICAgZGF5c0NSLm1vbnRocyA9IHBvb2xlZF94LAogICAgICBNVE1fbUwgPSAxMCBeIHByZWRpY3QocG9vbGVkX2ZpdCwgbmV3ZGF0YSA9IHRpYmJsZShkYXlzQ1IubW9udGhzID0gcG9vbGVkX3gpKQogICAgKQogIH0gZWxzZSB7CiAgICB0aWJibGUoZGF5c0NSLm1vbnRocyA9IG51bWVyaWMoMCksIE1UTV9tTCA9IG51bWVyaWMoMCkpCiAgfQp9Cgp4X21pbiA8LSBmbG9vcihtaW4oZGYkZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSAvIDMpICogMwp4X21heCA8LSBjZWlsaW5nKG1heChkZiRkYXlzQ1IubW9udGhzLCBuYS5ybSA9IFRSVUUpIC8gMykgKiAzCnlfbWluX3BvcyA8LSBtYXgobWluKGRmJE1UTV9tTFtkZiRNVE1fbUwgPiAwXSwgbmEucm0gPSBUUlVFKSAvIDIsIDAuMDEpCnlfbWF4X3BvcyA8LSAxMCBeIGNlaWxpbmcobG9nMTAobWF4KGRmJE1UTV9tTCwgbmEucm0gPSBUUlVFKSkpCmxvZ19icmVha3MgPC0gMTAgXiBzZXEoZmxvb3IobG9nMTAoeV9taW5fcG9zKSksIGNlaWxpbmcobG9nMTAoeV9tYXhfcG9zKSkpCgpwIDwtIGdncGxvdCgpICsKICAjIHBlci1wYXRpZW50IGZpdHRlZCBsaW5lcyAoaWYgYW55KQogIGdlb21fbGluZShkYXRhID0gcHJlZF9saW5lcywKICAgICAgICAgICAgYWVzKHggPSBkYXlzQ1IubW9udGhzLCB5ID0gTVRNX21MLCBjb2xvciA9IFBhdGllbnROYW1lKSwKICAgICAgICAgICAgbGluZXdpZHRoID0gMSkgKwogICMgcG9vbGVkIGRhc2hlZCB0cmVuZCAoaWYgYW55KQogIGdlb21fbGluZShkYXRhID0gcG9vbGVkX2xpbmUsCiAgICAgICAgICAgIGFlcyh4ID0gZGF5c0NSLm1vbnRocywgeSA9IE1UTV9tTCksCiAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuOCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleTM1IikgKwogICMgcmF3IHBvaW50cyAoYWxsIHRpbWVwb2ludHMsIGJlZm9yZSBhbmQgYWZ0ZXIpCiAgZ2VvbV9wb2ludChkYXRhID0gZGYsCiAgICAgICAgICAgICBhZXMoeCA9IGRheXNDUi5tb250aHMsIHkgPSBNVE1fbUwsIGNvbG9yID0gUGF0aWVudE5hbWUpLAogICAgICAgICAgICAgc2l6ZSA9IDEuOCwgYWxwaGEgPSAwLjg1KSArCiAgIyB2ZXJ0aWNhbCBsaW5lIGF0IGltYWdpbmctcG9zaXRpdmUgZGF0ZQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuOCwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSBsb2dfYnJlYWtzLAogICAgICAgICAgICAgICAgbGFiZWxzID0gbGFiZWxfbnVtYmVyKGFjY3VyYWN5ID0gMSkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKHhfbWluLCB4X21heCwgYnkgPSAzKSkgKyAgIyBldmVyeSAzIG1vbnRocwogIGxhYnMoCiAgICB4ID0gIk1vbnRocyBiZWZvcmUvYWZ0ZXIgY2xpbmljYWwgcmVjdXJyZW5jZSAoMCA9IGltYWdpbmcgcG9zaXRpdmUpIiwKICAgIHkgPSAiY3RETkEgbGV2ZWwgKE1UTS9tTCkiLAogICAgY29sb3IgPSAiUGF0aWVudCIKICApICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxMikgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5ODUiLCBsaW5ld2lkdGggPSAwLjMpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5OTIiLCBsaW5ld2lkdGggPSAwLjIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiICAjIGNoYW5nZSB0byAicmlnaHQiIGlmIHlvdSB3YW50IHRoZSBsZWdlbmQKICApCgpwcmludChwKQpgYGAKCgojUGF0aWVudCBTcGVjaWZpYyBQbG90IGZvciBVTk0tMDY5CmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiQ0xJQSBITlNDQ19QdCBTcGVjaWZpYyBQbG90IGRhdGEuY3N2IikKCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShzY2FsZXMpCgojIDEuIEZJTFRFUiBGT1IgUEFUSUVOVAp0YXJnZXRfcGF0aWVudCA8LSAiVU5NLTA2OSIKcGxvdF9kYXRhIDwtIGNpcmNfZGF0YSAlPiUgZmlsdGVyKFBhdGllbnROYW1lID09IHRhcmdldF9wYXRpZW50KQoKIyAyLiBQcm9jZXNzIERhdGEKIyBXZSBmaXggdGhlIHBzZXVkby16ZXJvIGZvciBsb2cgc2NhbGUgYnV0IGFsbG93IGhpZ2ggdmFsdWVzIChsaWtlIDE4LjA4KSB0byByZW1haW4KZGF0YV9TaWduYXRlcmEgPC0gcGxvdF9kYXRhICU+JSAKICBmaWx0ZXIoRXZlbnRfdHlwZSAlaW4lIGMoImN0RE5BX3BvcyIsICJjdEROQV9uZWciKSkgJT4lCiAgbXV0YXRlKE1UTV9Mb2cgPSBpZmVsc2UoTVRNLm1MID09IDAsIDAuMDAxLCBNVE0ubUwpKQoKZGF0YV9JbWFnaW5nX05FRCA8LSBwbG90X2RhdGEgJT4lIGZpbHRlcihFdmVudF90eXBlID09ICJJbWFnaW5nIiAmIEV2ZW50ID09ICJORUQiKQpkYXRhX0ltYWdpbmdfUEQgPC0gcGxvdF9kYXRhICU+JSBmaWx0ZXIoRXZlbnRfdHlwZSA9PSAiSW1hZ2luZyIgJiBFdmVudCA9PSAiUEQiKQpkYXRhX0Jpb3BzeSA8LSBwbG90X2RhdGEgJT4lIGZpbHRlcihFdmVudF90eXBlID09ICJCaW9wc3kiIHwgRXZlbnQgPT0gIkJpb3BzeSIpCgpkYXRhX1RyZWF0bWVudCA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoVHhfdHlwZSkgJiBUeF90eXBlICE9ICJOQSIgJiBUeF90eXBlICE9ICIiICYgIWlzLm5hKFR4X3N0YXJ0Lm1vbnRocykpICU+JQogIGRpc3RpbmN0KFR4X3R5cGUsIFR4X3N0YXJ0Lm1vbnRocywgVHhfZW5kLm1vbnRocykKCiMgMy4gQ3JlYXRlIHRoZSBQbG90CmdncGxvdCgpICsKICAjIFRyZWF0bWVudCBSZWN0YW5nbGVzIC0gRXhwYW5kZWQgeW1heCB0byBtYXRjaCBuZXcgc2NhbGUKICBnZW9tX3JlY3QoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHhtaW4gPSBUeF9zdGFydC5tb250aHMsIHhtYXggPSBUeF9lbmQubW9udGhzLCB5bWluID0gMC4wMDQsIHltYXggPSAxMDApLCAKICAgICAgICAgICAgZmlsbCA9ICJwdXJwbGUiLCBhbHBoYSA9IDAuMTUpICsKICBnZW9tX3RleHQoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHggPSAoVHhfc3RhcnQubW9udGhzICsgVHhfZW5kLm1vbnRocykvMiwgeSA9IDUwLCBsYWJlbCA9IFR4X3R5cGUpLCAKICAgICAgICAgICAgYW5nbGUgPSA5MCwgY29sb3IgPSAicHVycGxlIiwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgCiAgIyBjdEROQSBMaW5lIC0gTm93IGl0IHdpbGwgY29ubmVjdCBiZWNhdXNlIHRoZSBsaW1pdHMgYXJlIGhpZ2hlcgogIGdlb21fbGluZShkYXRhID0gZGF0YV9TaWduYXRlcmEsIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IE1UTV9Mb2cpLCAKICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjcpICsKICAKICAjIGN0RE5BIFBvaW50cwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfU2lnbmF0ZXJhLCAKICAgICAgICAgICAgIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IE1UTV9Mb2csIGZpbGwgPSBFdmVudF90eXBlKSwgCiAgICAgICAgICAgICBzaGFwZSA9IDIxLCBzaXplID0gMy41LCBjb2xvciA9ICJibGFjayIpICsKICAKICAjIENsaW5pY2FsIEV2ZW50cwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfSW1hZ2luZ19ORUQsIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IDAuMDA1LCBjb2xvciA9ICJORUQgaW4gU2NhbiIpLCAKICAgICAgICAgICAgIHNoYXBlID0gMjUsIHNpemUgPSA0LCBmaWxsID0gImdyZWVuIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfSW1hZ2luZ19QRCwgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCB5ID0gMC4wMDUsIGNvbG9yID0gIlBEIChQcm9ncmVzc2lvbikiKSwgCiAgICAgICAgICAgICBzaGFwZSA9IDI1LCBzaXplID0gNCwgZmlsbCA9ICJyZWQiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9CaW9wc3ksIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IDAuMDA1LCBjb2xvciA9ICJCaW9wc3kiKSwgCiAgICAgICAgICAgICBzaGFwZSA9IDIzLCBzaXplID0gNCwgZmlsbCA9ICJvcmFuZ2UiKSArCiAgCiAgIyBYLUF4aXM6IDMtbW9udGggaW50ZXJ2YWxzCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgocGxvdF9kYXRhJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtPVQpICsgNiwgYnkgPSAzKSkgKwogIAogICMgWS1BeGlzOiBFWFBBTkRFRCBMb2cxMCBzY2FsZSB0byBpbmNsdWRlIDE4LjA4CiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSBjKDAuMDAxLCAwLjAxLCAwLjEsIDEsIDEwLCAxMDApLAogICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMCIsICIwLjAxIiwgIjAuMSIsICIxIiwgIjEwIiwgIjEwMCIpLAogICAgICAgICAgICAgICAgbGltaXRzID0gYygwLjAwMSwgMTAwKSkgKwogIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImN0RE5BX3BvcyIgPSAiYmxhY2siLCAiY3RETkFfbmVnIiA9ICJ3aGl0ZSIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpLCBuYW1lID0gImN0RE5BIFN0YXR1cyIpICsKICAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTkVEIGluIFNjYW4iID0gImRhcmtncmVlbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQRCAoUHJvZ3Jlc3Npb24pIiA9ICJkYXJrcmVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJpb3BzeSIgPSAiZGFya29yYW5nZSIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDbGluaWNhbCBFdmVudHMiKSArCiAgCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSBjKDIzLCAyNSwgMjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gYygib3JhbmdlIiwgImdyZWVuIiwgInJlZCIpKSkpICsKICAKICBsYWJzKHggPSAiTW9udGhzIGZyb20gRGlhZ25vc2lzL1N1cmdlcnkiLCAKICAgICAgIHkgPSAiTVRNL21MIiwgCiAgICAgICB0aXRsZSA9IHBhc3RlKCJDbGluaWNhbCBUaW1lbGluZToiLCB0YXJnZXRfcGF0aWVudCkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoK